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.
This commit is contained in:
Dean Moldovan 2016-10-28 03:08:15 +02:00 committed by Wenzel Jakob
parent e18bc02fc9
commit c7ac16bb2e
8 changed files with 178 additions and 136 deletions

View File

@ -216,7 +216,7 @@ public:
auto const &type_dict = get_internals().registered_types_py;
bool new_style_class = PyType_Check(tobj);
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
tuple parents(tobj->tp_bases, true);
auto parents = reinterpret_borrow<tuple>(tobj->tp_bases);
for (handle parent : parents) {
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
if (result)
@ -237,7 +237,7 @@ public:
/* Perform an implicit conversion */
if (convert) {
for (auto &converter : typeinfo->implicit_conversions) {
temp = object(converter(src.ptr(), typeinfo->type), false);
temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
if (load(temp, false))
return true;
}
@ -284,7 +284,7 @@ public:
return handle((PyObject *) it_i->second).inc_ref();
}
object inst(PyType_GenericAlloc(tinfo->type, 0), false);
auto inst = reinterpret_steal<object>(PyType_GenericAlloc(tinfo->type, 0));
auto wrapper = (instance<void> *) inst.ptr();
@ -487,9 +487,9 @@ public:
#endif
PyErr_Clear();
if (type_error && PyNumber_Check(src.ptr())) {
object tmp(std::is_floating_point<T>::value
? PyNumber_Float(src.ptr())
: PyNumber_Long(src.ptr()), true);
auto tmp = reinterpret_borrow<object>(std::is_floating_point<T>::value
? PyNumber_Float(src.ptr())
: PyNumber_Long(src.ptr()));
PyErr_Clear();
return load(tmp, false);
}
@ -544,7 +544,7 @@ public:
/* Check if this is a capsule */
if (isinstance<capsule>(h)) {
value = capsule(h, true);
value = reinterpret_borrow<capsule>(h);
return true;
}
@ -596,7 +596,7 @@ public:
if (!src) {
return false;
} else if (PyUnicode_Check(load_src.ptr())) {
temp = object(PyUnicode_AsUTF8String(load_src.ptr()), false);
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(load_src.ptr()));
if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError
load_src = temp;
}
@ -637,7 +637,7 @@ public:
if (!src) {
return false;
} else if (!PyUnicode_Check(load_src.ptr())) {
temp = object(PyUnicode_FromObject(load_src.ptr()), false);
temp = reinterpret_steal<object>(PyUnicode_FromObject(load_src.ptr()));
if (!temp) { PyErr_Clear(); return false; }
load_src = temp;
}
@ -646,10 +646,10 @@ public:
#if PY_MAJOR_VERSION >= 3
buffer = PyUnicode_AsWideCharString(load_src.ptr(), &length);
#else
temp = object(
temp = reinterpret_steal<object>(
sizeof(wchar_t) == sizeof(short)
? PyUnicode_AsUTF16String(load_src.ptr())
: PyUnicode_AsUTF32String(load_src.ptr()), false);
: PyUnicode_AsUTF32String(load_src.ptr()));
if (temp) {
int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), (char **) &buffer, &length);
if (err == -1) { buffer = nullptr; } // TypeError
@ -730,8 +730,8 @@ public:
}
static handle cast(const type &src, return_value_policy policy, handle parent) {
object o1 = object(make_caster<T1>::cast(src.first, policy, parent), false);
object o2 = object(make_caster<T2>::cast(src.second, policy, parent), false);
auto o1 = reinterpret_steal<object>(make_caster<T1>::cast(src.first, policy, parent));
auto o2 = reinterpret_steal<object>(make_caster<T2>::cast(src.second, policy, parent));
if (!o1 || !o2)
return handle();
tuple result(2);
@ -846,7 +846,7 @@ protected:
/* Implementation: Convert a C++ tuple into a Python tuple */
template <size_t ... Indices> static handle cast(const type &src, return_value_policy policy, handle parent, index_sequence<Indices...>) {
std::array<object, size> entries {{
object(make_caster<Tuple>::cast(std::get<Indices>(src), policy, parent), false)...
reinterpret_steal<object>(make_caster<Tuple>::cast(std::get<Indices>(src), policy, parent))...
}};
for (const auto &entry: entries)
if (!entry)
@ -905,7 +905,7 @@ public:
auto const &type_dict = get_internals().registered_types_py;
bool new_style_class = PyType_Check(tobj);
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
tuple parents(tobj->tp_bases, true);
auto parents = reinterpret_borrow<tuple>(tobj->tp_bases);
for (handle parent : parents) {
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
if (result)
@ -919,7 +919,7 @@ public:
if (convert) {
for (auto &converter : typeinfo->implicit_conversions) {
temp = object(converter(src.ptr(), typeinfo->type), false);
temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
if (load(temp, false))
return true;
}
@ -1000,7 +1000,7 @@ struct pyobject_caster {
bool load(handle src, bool /* convert */) {
if (!isinstance<type>(src))
return false;
value = type(src, true);
value = reinterpret_borrow<type>(src);
return true;
}
@ -1070,6 +1070,7 @@ template <typename T> make_caster<T> load_type(const handle &handle) {
NAMESPACE_END(detail)
// pytype -> C++ type
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
T cast(const handle &handle) {
static_assert(!detail::cast_is_temporary_value_reference<T>::value,
@ -1078,9 +1079,11 @@ T cast(const handle &handle) {
return detail::load_type<T>(handle).operator typename type_caster::template cast_op_type<T>();
}
// pytype -> pytype (calls converting constructor)
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
T cast(const handle &handle) { return {handle, true}; }
T cast(const handle &handle) { return T(reinterpret_borrow<object>(handle)); }
// C++ type -> py::object
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference,
handle parent = handle()) {
@ -1088,7 +1091,7 @@ object cast(const T &value, return_value_policy policy = return_value_policy::au
policy = std::is_pointer<T>::value ? return_value_policy::take_ownership : return_value_policy::copy;
else if (policy == return_value_policy::automatic_reference)
policy = std::is_pointer<T>::value ? return_value_policy::reference : return_value_policy::copy;
return object(detail::make_caster<T>::cast(value, policy, parent), false);
return reinterpret_steal<object>(detail::make_caster<T>::cast(value, policy, parent));
}
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,
typename... Args> tuple make_tuple(Args&&... args_) {
const size_t size = sizeof...(Args);
std::array<object, size> args {
{ object(detail::make_caster<Args>::cast(
std::forward<Args>(args_), policy, nullptr), false)... }
{ reinterpret_steal<object>(detail::make_caster<Args>::cast(
std::forward<Args>(args_), policy, nullptr))... }
};
for (auto &arg_value : args) {
if (!arg_value) {
@ -1195,7 +1198,9 @@ struct arg_v : arg {
template <typename T>
arg_v(const char *name, T &&x, const char *descr = nullptr)
: arg(name),
value(detail::make_caster<T>::cast(x, return_value_policy::automatic, handle()), false),
value(reinterpret_steal<object>(
detail::make_caster<T>::cast(x, return_value_policy::automatic, {})
)),
descr(descr)
#if !defined(NDEBUG)
, type(type_id<T>())
@ -1256,10 +1261,10 @@ public:
/// Call a Python function and pass the collected arguments
object call(PyObject *ptr) const {
auto result = object(PyObject_CallObject(ptr, m_args.ptr()), false);
PyObject *result = PyObject_CallObject(ptr, m_args.ptr());
if (!result)
throw error_already_set();
return result;
return reinterpret_steal<object>(result);
}
private:
@ -1289,16 +1294,16 @@ public:
/// Call a Python function and pass the collected arguments
object call(PyObject *ptr) const {
auto result = object(PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()), false);
PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr());
if (!result)
throw error_already_set();
return result;
return reinterpret_steal<object>(result);
}
private:
template <typename T>
void process(list &args_list, T &&x) {
auto o = object(detail::make_caster<T>::cast(std::forward<T>(x), policy, nullptr), false);
auto o = reinterpret_steal<object>(detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
if (!o) {
#if defined(NDEBUG)
argument_cast_error();
@ -1335,7 +1340,7 @@ private:
void process(list &/*args_list*/, detail::kwargs_proxy kp) {
if (!kp)
return;
for (const auto &k : dict(kp, true)) {
for (const auto &k : reinterpret_borrow<dict>(kp)) {
if (m_kwargs.contains(k.first)) {
#if defined(NDEBUG)
multiple_values_error();

View File

@ -182,7 +182,7 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
if (!src)
return false;
object obj(src, true);
auto obj = reinterpret_borrow<object>(src);
object sparse_module = module::import("scipy.sparse");
object matrix_type = sparse_module.attr(
rowMajor ? "csr_matrix" : "csc_matrix");

View File

@ -31,7 +31,7 @@ enum eval_mode {
template <eval_mode mode = eval_expr>
object eval(str expr, object global = object(), object local = object()) {
if (!global) {
global = object(PyEval_GetGlobals(), true);
global = reinterpret_borrow<object>(PyEval_GetGlobals());
if (!global)
global = dict();
}
@ -50,17 +50,16 @@ object eval(str expr, object global = object(), object local = object()) {
default: pybind11_fail("invalid evaluation mode");
}
object result(PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()), false);
PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
if (!result)
throw error_already_set();
return result;
return reinterpret_steal<object>(result);
}
template <eval_mode mode = eval_statements>
object eval_file(str fname, object global = object(), object local = object()) {
if (!global) {
global = object(PyEval_GetGlobals(), true);
global = reinterpret_borrow<object>(PyEval_GetGlobals());
if (!global)
global = dict();
}
@ -83,9 +82,9 @@ object eval_file(str fname, object global = object(), object local = object()) {
FILE *f = _Py_fopen(fname.ptr(), "r");
#else
/* No unicode support in open() :( */
object fobj(PyFile_FromString(
auto fobj = reinterpret_steal<object>(PyFile_FromString(
const_cast<char *>(fname_str.c_str()),
const_cast<char*>("r")), false);
const_cast<char*>("r")));
FILE *f = nullptr;
if (fobj)
f = PyFile_AsFile(fobj.ptr());
@ -96,14 +95,11 @@ object eval_file(str fname, object global = object(), object local = object()) {
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
}
object result(PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(),
local.ptr(), closeFile),
false);
PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(),
local.ptr(), closeFile);
if (!result)
throw error_already_set();
return result;
return reinterpret_steal<object>(result);
}
NAMESPACE_END(pybind11)

View File

@ -36,7 +36,7 @@ public:
captured variables), in which case the roundtrip can be avoided.
*/
if (PyCFunction_Check(src_.ptr())) {
capsule c(PyCFunction_GetSelf(src_.ptr()), true);
auto c = reinterpret_borrow<capsule>(PyCFunction_GetSelf(src_.ptr()));
auto rec = (function_record *) c;
using FunctionType = Return (*) (Args...);
@ -47,7 +47,7 @@ public:
}
}
object src(src_, true);
auto src = reinterpret_borrow<object>(src_);
value = [src](Args... args) -> Return {
gil_scoped_acquire acq;
object retval(src(std::move(args)...));

View File

@ -239,7 +239,7 @@ public:
PyObject *ptr = nullptr;
if (!detail::npy_api::get().PyArray_DescrConverter_(args.release().ptr(), &ptr) || !ptr)
throw error_already_set();
return object(ptr, false);
return reinterpret_steal<dtype>(ptr);
}
/// Return dtype associated with a C++ type.
@ -266,7 +266,7 @@ private:
static object _dtype_from_pep3118() {
static PyObject *obj = module::import("numpy.core._internal")
.attr("_dtype_from_pep3118").cast<object>().release().ptr();
return object(obj, true);
return reinterpret_borrow<object>(obj);
}
dtype strip_padding() {
@ -279,7 +279,7 @@ private:
std::vector<field_descr> field_descriptors;
for (auto field : attr("fields").attr("items")()) {
auto spec = object(field, true).cast<tuple>();
auto spec = field.cast<tuple>();
auto name = spec[0].cast<pybind11::str>();
auto format = spec[1].cast<tuple>()[0].cast<dtype>();
auto offset = spec[1].cast<tuple>()[1].cast<pybind11::int_>();
@ -326,22 +326,22 @@ public:
if (base && ptr) {
if (isinstance<array>(base))
/* Copy flags from base (except baseship bit) */
flags = array(base, true).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_;
flags = reinterpret_borrow<array>(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_;
else
/* Writable by default, easy to downgrade later on if needed */
flags = detail::npy_api::NPY_ARRAY_WRITEABLE_;
}
object tmp(api.PyArray_NewFromDescr_(
auto tmp = reinterpret_steal<object>(api.PyArray_NewFromDescr_(
api.PyArray_Type_, descr.release().ptr(), (int) ndim, (Py_intptr_t *) shape.data(),
(Py_intptr_t *) strides.data(), const_cast<void *>(ptr), flags, nullptr), false);
(Py_intptr_t *) strides.data(), const_cast<void *>(ptr), flags, nullptr));
if (!tmp)
pybind11_fail("NumPy: unable to create array!");
if (ptr) {
if (base) {
PyArray_GET_(tmp.ptr(), base) = base.inc_ref().ptr();
} else {
tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false);
tmp = reinterpret_steal<object>(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */));
}
}
m_ptr = tmp.release().ptr();
@ -374,7 +374,7 @@ public:
/// Array descriptor (dtype)
pybind11::dtype dtype() const {
return object(PyArray_GET_(m_ptr, descr), true);
return reinterpret_borrow<pybind11::dtype>(PyArray_GET_(m_ptr, descr));
}
/// Total number of elements
@ -399,7 +399,7 @@ public:
/// Base object
object base() const {
return object(PyArray_GET_(m_ptr, base), true);
return reinterpret_borrow<object>(PyArray_GET_(m_ptr, base));
}
/// Dimensions of the array
@ -474,14 +474,14 @@ public:
/// Return a new view with all of the dimensions of length 1 removed
array squeeze() {
auto& api = detail::npy_api::get();
return array(api.PyArray_Squeeze_(m_ptr), false);
return reinterpret_steal<array>(api.PyArray_Squeeze_(m_ptr));
}
/// Ensure that the argument is a NumPy array
static array ensure(object input, int ExtraFlags = 0) {
auto& api = detail::npy_api::get();
return array(api.PyArray_FromAny_(
input.release().ptr(), nullptr, 0, 0, detail::npy_api::NPY_ENSURE_ARRAY_ | ExtraFlags, nullptr), false);
return reinterpret_steal<array>(api.PyArray_FromAny_(
input.release().ptr(), nullptr, 0, 0, detail::npy_api::NPY_ENSURE_ARRAY_ | ExtraFlags, nullptr));
}
protected:
@ -542,7 +542,7 @@ template <typename T, int ExtraFlags = array::forcecast> class array_t : public
public:
array_t() : array() { }
array_t(handle h, bool borrowed) : array(h, borrowed) { m_ptr = ensure_(m_ptr); }
array_t(handle h, bool is_borrowed) : array(h, is_borrowed) { m_ptr = ensure_(m_ptr); }
array_t(const object &o) : array(o) { m_ptr = ensure_(m_ptr); }
@ -668,7 +668,7 @@ public:
enum { value = values[detail::log2(sizeof(T)) * 2 + (std::is_unsigned<T>::value ? 1 : 0)] };
static pybind11::dtype dtype() {
if (auto ptr = npy_api::get().PyArray_DescrFromType_(value))
return object(ptr, true);
return reinterpret_borrow<pybind11::dtype>(ptr);
pybind11_fail("Unsupported buffer format!");
}
template <typename T2 = T, enable_if_t<std::is_signed<T2>::value, int> = 0>
@ -683,7 +683,7 @@ template <typename T> constexpr const int npy_format_descriptor<
enum { value = npy_api::NumPyName }; \
static pybind11::dtype dtype() { \
if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) \
return object(ptr, true); \
return reinterpret_borrow<pybind11::dtype>(ptr); \
pybind11_fail("Unsupported buffer format!"); \
} \
static PYBIND11_DESCR name() { return _(Name); } }
@ -778,7 +778,7 @@ struct npy_format_descriptor<T, enable_if_t<is_pod_struct<T>::value>> {
static PYBIND11_DESCR name() { return _("struct"); }
static pybind11::dtype dtype() {
return object(dtype_ptr(), true);
return reinterpret_borrow<pybind11::dtype>(dtype_ptr());
}
static std::string format() {
@ -801,7 +801,7 @@ private:
auto& api = npy_api::get();
if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_))
return false;
if (auto descr = object(api.PyArray_DescrFromScalar_(obj), false)) {
if (auto descr = reinterpret_steal<object>(api.PyArray_DescrFromScalar_(obj))) {
if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) {
value = ((PyVoidScalarObject_Proxy *) obj)->obval;
return true;

View File

@ -256,7 +256,7 @@ protected:
detail::function_record *chain = nullptr, *chain_start = rec;
if (rec->sibling) {
if (PyCFunction_Check(rec->sibling.ptr())) {
capsule rec_capsule(PyCFunction_GetSelf(rec->sibling.ptr()), true);
auto rec_capsule = reinterpret_borrow<capsule>(PyCFunction_GetSelf(rec->sibling.ptr()));
chain = (detail::function_record *) rec_capsule;
/* Never append a method to an overload chain of a parent class;
instead, hide the parent's overloads in this case */
@ -382,7 +382,7 @@ protected:
result = PYBIND11_TRY_NEXT_OVERLOAD;
try {
for (; it != nullptr; it = it->next) {
tuple args_(args, true);
auto args_ = reinterpret_borrow<tuple>(args);
size_t kwargs_consumed = 0;
/* For each overload:
@ -502,7 +502,7 @@ protected:
msg += "\n";
}
msg += "\nInvoked with: ";
tuple args_(args, true);
auto args_ = reinterpret_borrow<tuple>(args);
for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) {
msg += static_cast<std::string>(pybind11::str(args_[ti]));
if ((ti + 1) != args_.size() )
@ -565,7 +565,7 @@ public:
module def_submodule(const char *name, const char *doc = nullptr) {
std::string full_name = std::string(PyModule_GetName(m_ptr))
+ std::string(".") + std::string(name);
module result(PyImport_AddModule(full_name.c_str()), true);
auto result = reinterpret_borrow<module>(PyImport_AddModule(full_name.c_str()));
if (doc && options::show_user_defined_docstrings())
result.attr("__doc__") = pybind11::str(doc);
attr(name) = result;
@ -576,7 +576,7 @@ public:
PyObject *obj = PyImport_ImportModule(name);
if (!obj)
throw import_error("Module \"" + std::string(name) + "\" not found!");
return module(obj, false);
return reinterpret_steal<module>(obj);
}
// Adds an object to the module using the given name. Throws if an object with the given name
@ -636,7 +636,7 @@ protected:
pybind11_fail("generic_type: type \"" + std::string(rec->name) +
"\" is already registered!");
object name(PYBIND11_FROM_STRING(rec->name), false);
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec->name));
object scope_module;
if (rec->scope) {
if (hasattr(rec->scope, rec->name))
@ -657,8 +657,8 @@ protected:
scope_qualname = rec->scope.attr("__qualname__");
object ht_qualname;
if (scope_qualname) {
ht_qualname = object(PyUnicode_FromFormat(
"%U.%U", scope_qualname.ptr(), name.ptr()), false);
ht_qualname = reinterpret_steal<object>(PyUnicode_FromFormat(
"%U.%U", scope_qualname.ptr(), name.ptr()));
} else {
ht_qualname = name;
}
@ -684,7 +684,7 @@ protected:
garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */
object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
auto type_holder = reinterpret_steal<object>(PyType_Type.tp_alloc(&PyType_Type, 0));
auto type = (PyHeapTypeObject*) type_holder.ptr();
if (!type_holder || !name)
@ -768,7 +768,7 @@ protected:
/// Helper function which tags all parents of a type using mult. inheritance
void mark_parents_nonsimple(PyTypeObject *value) {
tuple t(value->tp_bases, true);
auto t = reinterpret_borrow<tuple>(value->tp_bases);
for (handle h : t) {
auto tinfo2 = get_type_info((PyTypeObject *) h.ptr());
if (tinfo2)
@ -785,10 +785,10 @@ protected:
if (ob_type == &PyType_Type) {
std::string name_ = std::string(ht_type.tp_name) + "__Meta";
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
object ht_qualname(PyUnicode_FromFormat("%U__Meta", attr("__qualname__").ptr()), false);
auto ht_qualname = reinterpret_steal<object>(PyUnicode_FromFormat("%U__Meta", attr("__qualname__").ptr()));
#endif
object name(PYBIND11_FROM_STRING(name_.c_str()), false);
object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(name_.c_str()));
auto type_holder = reinterpret_steal<object>(PyType_Type.tp_alloc(&PyType_Type, 0));
if (!type_holder || !name)
pybind11_fail("generic_type::metaclass(): unable to create type object!");
@ -936,7 +936,7 @@ public:
static_assert(detail::all_of_t<is_valid_class_option, options...>::value,
"Unknown/invalid class_ template parameters provided");
PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check)
PYBIND11_OBJECT(class_, generic_type, PyType_Check)
template <typename... Extra>
class_(handle scope, const char *name, const Extra &... extra) {
@ -1117,9 +1117,9 @@ public:
}
}
pybind11::str doc_obj = pybind11::str((rec_fget->doc && pybind11::options::show_user_defined_docstrings()) ? rec_fget->doc : "");
object property(
const auto property = reinterpret_steal<object>(
PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false);
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr));
if (rec_fget->class_)
attr(name) = property;
else
@ -1178,8 +1178,8 @@ private:
static detail::function_record *get_function_record(handle h) {
h = detail::get_function(h);
return h ? (detail::function_record *) capsule(
PyCFunction_GetSelf(h.ptr()), true) : nullptr;
return h ? (detail::function_record *) reinterpret_borrow<capsule>(PyCFunction_GetSelf(h.ptr()))
: nullptr;
}
};

View File

@ -102,9 +102,9 @@ protected:
class object : public handle {
public:
object() = default;
PYBIND11_DEPRECATED("Use reinterpret_borrow<object>() or reinterpret_steal<object>()")
object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); }
object(const object &o) : handle(o) { inc_ref(); }
object(const handle &h, bool borrowed) : handle(h) { if (borrowed) inc_ref(); }
object(PyObject *ptr, bool borrowed) : handle(ptr) { if (borrowed) inc_ref(); }
object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; }
~object() { dec_ref(); }
@ -135,8 +135,26 @@ public:
template <typename T> T cast() const &;
// Calling on an object rvalue does a move, if needed and/or possible
template <typename T> T cast() &&;
protected:
// Tags for choosing constructors from raw PyObject *
struct borrowed_t { }; static constexpr borrowed_t borrowed{};
struct stolen_t { }; static constexpr stolen_t stolen{};
template <typename T> friend T reinterpret_borrow(handle);
template <typename T> friend T reinterpret_steal(handle);
public:
// Only accessible from derived classes and the reinterpret_* functions
object(handle h, borrowed_t) : handle(h) { inc_ref(); }
object(handle h, stolen_t) : handle(h) { }
};
/** The following functions don't do any kind of conversion, they simply declare
that a PyObject is a certain type and borrow or steal the reference. */
template <typename T> T reinterpret_borrow(handle h) { return {h, object::borrowed}; }
template <typename T> T reinterpret_steal(handle h) { return {h, object::stolen}; }
/// Check if `obj` is an instance of type `T`
template <typename T, detail::enable_if_t<std::is_base_of<object, T>::value, int> = 0>
bool isinstance(handle obj) { return T::_check(obj); }
@ -158,30 +176,30 @@ inline bool hasattr(handle obj, const char *name) {
inline object getattr(handle obj, handle name) {
PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr());
if (!result) { throw error_already_set(); }
return {result, false};
return reinterpret_steal<object>(result);
}
inline object getattr(handle obj, const char *name) {
PyObject *result = PyObject_GetAttrString(obj.ptr(), name);
if (!result) { throw error_already_set(); }
return {result, false};
return reinterpret_steal<object>(result);
}
inline object getattr(handle obj, handle name, handle default_) {
if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) {
return {result, false};
return reinterpret_steal<object>(result);
} else {
PyErr_Clear();
return {default_, true};
return reinterpret_borrow<object>(default_);
}
}
inline object getattr(handle obj, const char *name, handle default_) {
if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) {
return {result, false};
return reinterpret_steal<object>(result);
} else {
PyErr_Clear();
return {default_, true};
return reinterpret_borrow<object>(default_);
}
}
@ -218,7 +236,7 @@ public:
void operator=(const object &o) && { std::move(*this).operator=(handle(o)); }
void operator=(const object &o) & { operator=(handle(o)); }
void operator=(handle value) && { Policy::set(obj, key, value); }
void operator=(handle value) & { get_cache() = object(value, true); }
void operator=(handle value) & { get_cache() = reinterpret_borrow<object>(value); }
template <typename T = Policy>
PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)")
@ -267,7 +285,7 @@ struct generic_item {
static object get(handle obj, handle key) {
PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr());
if (!result) { throw error_already_set(); }
return {result, false};
return reinterpret_steal<object>(result);
}
static void set(handle obj, handle key, handle val) {
@ -281,7 +299,7 @@ struct sequence_item {
static object get(handle obj, size_t index) {
PyObject *result = PySequence_GetItem(obj.ptr(), static_cast<ssize_t>(index));
if (!result) { throw error_already_set(); }
return {result, true};
return reinterpret_borrow<object>(result);
}
static void set(handle obj, size_t index, handle val) {
@ -298,7 +316,7 @@ struct list_item {
static object get(handle obj, size_t index) {
PyObject *result = PyList_GetItem(obj.ptr(), static_cast<ssize_t>(index));
if (!result) { throw error_already_set(); }
return {result, true};
return reinterpret_borrow<object>(result);
}
static void set(handle obj, size_t index, handle val) {
@ -315,7 +333,7 @@ struct tuple_item {
static object get(handle obj, size_t index) {
PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index));
if (!result) { throw error_already_set(); }
return {result, true};
return reinterpret_borrow<object>(result);
}
static void set(handle obj, size_t index, handle val) {
@ -390,23 +408,30 @@ class unpacking_collector;
NAMESPACE_END(detail)
// TODO: After the deprecated constructors are removed, this macro can be simplified by
// inheriting ctors: `using Parent::Parent`. It's not an option right now because
// the `using` statement triggers the parent deprecation warning even if the ctor
// isn't even used.
#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
public: \
PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \
Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed) : Parent(h, stolen)) { } \
Name(handle h, borrowed_t) : Parent(h, borrowed) { } \
Name(handle h, stolen_t) : Parent(h, stolen) { } \
PYBIND11_DEPRECATED("Use py::isinstance<py::python_type>(obj) instead") \
bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \
static bool _check(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); }
#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
Name(handle h, bool borrowed) : Name(object(h, borrowed)) { } \
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
Name(const object &o) : Parent(ConvertFun(o.ptr()), false) { if (!m_ptr) throw error_already_set(); }
Name(const object &o) : Parent(ConvertFun(o.ptr()), stolen) { if (!m_ptr) throw error_already_set(); }
#define PYBIND11_OBJECT(Name, Parent, CheckFun) \
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
Name(handle h, bool borrowed) : Parent(h, borrowed) { } \
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
Name(const object &o) : Parent(o) { }
Name(const object &o) : Parent(o) { } \
Name(object &&o) : Parent(std::move(o)) { }
#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \
PYBIND11_OBJECT(Name, Parent, CheckFun) \
@ -448,7 +473,7 @@ public:
}
private:
void advance() { value = object(PyIter_Next(m_ptr), false); }
void advance() { value = reinterpret_steal<object>(PyIter_Next(m_ptr)); }
private:
object value = {};
@ -467,13 +492,13 @@ public:
PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str)
str(const char *c, size_t n)
: object(PyUnicode_FromStringAndSize(c, (ssize_t) n), false) {
: object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate string object!");
}
// 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects
str(const char *c = "")
: object(PyUnicode_FromString(c), false) {
: object(PyUnicode_FromString(c), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate string object!");
}
@ -481,12 +506,12 @@ public:
explicit str(const bytes &b);
explicit str(handle h) : object(raw_str(h.ptr()), false) { }
explicit str(handle h) : object(raw_str(h.ptr()), stolen) { }
operator std::string() const {
object temp = *this;
if (PyUnicode_Check(m_ptr)) {
temp = object(PyUnicode_AsUTF8String(m_ptr), false);
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(m_ptr));
if (!temp)
pybind11_fail("Unable to extract string contents! (encoding issue)");
}
@ -526,12 +551,12 @@ public:
// Allow implicit conversion:
bytes(const char *c = "")
: object(PYBIND11_BYTES_FROM_STRING(c), false) {
: object(PYBIND11_BYTES_FROM_STRING(c), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
}
bytes(const char *c, size_t n)
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), false) {
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
}
@ -552,7 +577,7 @@ public:
inline bytes::bytes(const pybind11::str &s) {
object temp = s;
if (PyUnicode_Check(s.ptr())) {
temp = object(PyUnicode_AsUTF8String(s.ptr()), false);
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(s.ptr()));
if (!temp)
pybind11_fail("Unable to extract string contents! (encoding issue)");
}
@ -560,7 +585,7 @@ inline bytes::bytes(const pybind11::str &s) {
ssize_t length;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
pybind11_fail("Unable to extract string contents! (invalid type)");
auto obj = object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length), false);
auto obj = reinterpret_steal<object>(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length));
if (!obj)
pybind11_fail("Could not allocate bytes object!");
m_ptr = obj.release().ptr();
@ -571,7 +596,7 @@ inline str::str(const bytes& b) {
ssize_t length;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length))
pybind11_fail("Unable to extract bytes contents!");
auto obj = object(PyUnicode_FromStringAndSize(buffer, (ssize_t) length), false);
auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, (ssize_t) length));
if (!obj)
pybind11_fail("Could not allocate string object!");
m_ptr = obj.release().ptr();
@ -580,15 +605,15 @@ inline str::str(const bytes& b) {
class none : public object {
public:
PYBIND11_OBJECT(none, object, detail::PyNone_Check)
none() : object(Py_None, true) { }
none() : object(Py_None, borrowed) { }
};
class bool_ : public object {
public:
PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool)
bool_() : object(Py_False, true) { }
bool_() : object(Py_False, borrowed) { }
// Allow implicit conversion from and to `bool`:
bool_(bool value) : object(value ? Py_True : Py_False, true) { }
bool_(bool value) : object(value ? Py_True : Py_False, borrowed) { }
operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; }
private:
@ -603,7 +628,7 @@ private:
class int_ : public object {
public:
PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long)
int_() : object(PyLong_FromLong(0), false) { }
int_() : object(PyLong_FromLong(0), stolen) { }
// Allow implicit conversion from C++ integral types:
template <typename T,
detail::enable_if_t<std::is_integral<T>::value, int> = 0>
@ -643,10 +668,10 @@ class float_ : public object {
public:
PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float)
// Allow implicit conversion from float/double:
float_(float value) : object(PyFloat_FromDouble((double) value), false) {
float_(float value) : object(PyFloat_FromDouble((double) value), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate float object!");
}
float_(double value = .0) : object(PyFloat_FromDouble((double) value), false) {
float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate float object!");
}
operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
@ -656,7 +681,8 @@ public:
class weakref : public object {
public:
PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check)
explicit weakref(handle obj, handle callback = handle()) : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), false) {
explicit weakref(handle obj, handle callback = {})
: object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate weak reference!");
}
};
@ -681,9 +707,10 @@ public:
class capsule : public object {
public:
PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
capsule(PyObject *obj, bool borrowed) : object(obj, borrowed) { }
PYBIND11_DEPRECATED("Use reinterpret_borrow<capsule>() or reinterpret_steal<capsule>()")
capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed) : object(ptr, stolen)) { }
explicit capsule(const void *value, void (*destruct)(PyObject *) = nullptr)
: object(PyCapsule_New(const_cast<void*>(value), nullptr, destruct), false) {
: object(PyCapsule_New(const_cast<void*>(value), nullptr, destruct), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate capsule object!");
}
template <typename T> operator T *() const {
@ -696,7 +723,7 @@ public:
class tuple : public object {
public:
PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple)
explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), false) {
explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
}
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
@ -706,7 +733,7 @@ public:
class dict : public object {
public:
PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict)
dict() : object(PyDict_New(), false) {
dict() : object(PyDict_New(), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate dict object!");
}
template <typename... Args,
@ -741,7 +768,7 @@ public:
class list : public object {
public:
PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List)
explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), false) {
explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate list object!");
}
size_t size() const { return (size_t) PyList_Size(m_ptr); }
@ -755,7 +782,7 @@ class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check)
class set : public object {
public:
PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New)
set() : object(PySet_New(nullptr), false) {
set() : object(PySet_New(nullptr), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate set object!");
}
size_t size() const { return (size_t) PySet_Size(m_ptr); }
@ -833,17 +860,31 @@ inline str repr(handle h) {
Py_XDECREF(str_value); str_value = unicode;
if (!str_value) throw error_already_set();
#endif
return {str_value, false};
return reinterpret_steal<str>(str_value);
}
NAMESPACE_BEGIN(detail)
template <typename D> iterator object_api<D>::begin() const { return {PyObject_GetIter(derived().ptr()), false}; }
template <typename D> iterator object_api<D>::end() const { return {nullptr, false}; }
template <typename D> item_accessor object_api<D>::operator[](handle key) const { return {derived(), object(key, true)}; }
template <typename D> item_accessor object_api<D>::operator[](const char *key) const { return {derived(), pybind11::str(key)}; }
template <typename D> obj_attr_accessor object_api<D>::attr(handle key) const { return {derived(), object(key, true)}; }
template <typename D> str_attr_accessor object_api<D>::attr(const char *key) const { return {derived(), key}; }
template <typename D> args_proxy object_api<D>::operator*() const { return args_proxy(derived().ptr()); }
template <typename D> iterator object_api<D>::begin() const {
return reinterpret_steal<iterator>(PyObject_GetIter(derived().ptr()));
}
template <typename D> iterator object_api<D>::end() const {
return {};
}
template <typename D> item_accessor object_api<D>::operator[](handle key) const {
return {derived(), reinterpret_borrow<object>(key)};
}
template <typename D> item_accessor object_api<D>::operator[](const char *key) const {
return {derived(), pybind11::str(key)};
}
template <typename D> obj_attr_accessor object_api<D>::attr(handle key) const {
return {derived(), reinterpret_borrow<object>(key)};
}
template <typename D> str_attr_accessor object_api<D>::attr(const char *key) const {
return {derived(), key};
}
template <typename D> args_proxy object_api<D>::operator*() const {
return args_proxy(derived().ptr());
}
template <typename D> template <typename T> bool object_api<D>::contains(T &&key) const {
return attr("__contains__")(std::forward<T>(key)).template cast<bool>();
}

View File

@ -47,7 +47,7 @@ template <typename Type, typename Key> struct set_caster {
bool load(handle src, bool convert) {
if (!isinstance<pybind11::set>(src))
return false;
pybind11::set s(src, true);
auto s = reinterpret_borrow<pybind11::set>(src);
value.clear();
key_conv conv;
for (auto entry : s) {
@ -61,7 +61,7 @@ template <typename Type, typename Key> struct set_caster {
static handle cast(const type &src, return_value_policy policy, handle parent) {
pybind11::set s;
for (auto const &value: src) {
object value_ = object(key_conv::cast(value, policy, parent), false);
auto value_ = reinterpret_steal<object>(key_conv::cast(value, policy, parent));
if (!value_ || !s.add(value_))
return handle();
}
@ -79,7 +79,7 @@ template <typename Type, typename Key, typename Value> struct map_caster {
bool load(handle src, bool convert) {
if (!isinstance<dict>(src))
return false;
dict d(src, true);
auto d = reinterpret_borrow<dict>(src);
key_conv kconv;
value_conv vconv;
value.clear();
@ -95,8 +95,8 @@ template <typename Type, typename Key, typename Value> struct map_caster {
static handle cast(const type &src, return_value_policy policy, handle parent) {
dict d;
for (auto const &kv: src) {
object key = object(key_conv::cast(kv.first, policy, parent), false);
object value = object(value_conv::cast(kv.second, policy, parent), false);
auto key = reinterpret_steal<object>(key_conv::cast(kv.first, policy, parent));
auto value = reinterpret_steal<object>(value_conv::cast(kv.second, policy, parent));
if (!key || !value)
return handle();
d[key] = value;
@ -114,7 +114,7 @@ template <typename Type, typename Value> struct list_caster {
bool load(handle src, bool convert) {
if (!isinstance<sequence>(src))
return false;
sequence s(src, true);
auto s = reinterpret_borrow<sequence>(src);
value_conv conv;
value.clear();
reserve_maybe(s, &value);
@ -135,7 +135,7 @@ template <typename Type, typename Value> struct list_caster {
list l(src.size());
size_t index = 0;
for (auto const &value: src) {
object value_ = object(value_conv::cast(value, policy, parent), false);
auto value_ = reinterpret_steal<object>(value_conv::cast(value, policy, parent));
if (!value_)
return handle();
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
@ -159,7 +159,7 @@ template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>>
bool load(handle src, bool convert) {
if (!isinstance<list>(src))
return false;
list l(src, true);
auto l = reinterpret_borrow<list>(src);
if (l.size() != Size)
return false;
value_conv conv;
@ -176,7 +176,7 @@ template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>>
list l(Size);
size_t index = 0;
for (auto const &value: src) {
object value_ = object(value_conv::cast(value, policy, parent), false);
auto value_ = reinterpret_steal<object>(value_conv::cast(value, policy, parent));
if (!value_)
return handle();
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference