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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -102,9 +102,9 @@ protected:
class object : public handle { class object : public handle {
public: public:
object() = default; 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 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(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; }
~object() { dec_ref(); } ~object() { dec_ref(); }
@ -135,8 +135,26 @@ public:
template <typename T> T cast() const &; template <typename T> T cast() const &;
// Calling on an object rvalue does a move, if needed and/or possible // Calling on an object rvalue does a move, if needed and/or possible
template <typename T> T cast() &&; 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` /// 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> template <typename T, detail::enable_if_t<std::is_base_of<object, T>::value, int> = 0>
bool isinstance(handle obj) { return T::_check(obj); } 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) { inline object getattr(handle obj, handle name) {
PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr());
if (!result) { throw error_already_set(); } if (!result) { throw error_already_set(); }
return {result, false}; return reinterpret_steal<object>(result);
} }
inline object getattr(handle obj, const char *name) { inline object getattr(handle obj, const char *name) {
PyObject *result = PyObject_GetAttrString(obj.ptr(), name); PyObject *result = PyObject_GetAttrString(obj.ptr(), name);
if (!result) { throw error_already_set(); } if (!result) { throw error_already_set(); }
return {result, false}; return reinterpret_steal<object>(result);
} }
inline object getattr(handle obj, handle name, handle default_) { inline object getattr(handle obj, handle name, handle default_) {
if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) {
return {result, false}; return reinterpret_steal<object>(result);
} else { } else {
PyErr_Clear(); PyErr_Clear();
return {default_, true}; return reinterpret_borrow<object>(default_);
} }
} }
inline object getattr(handle obj, const char *name, handle default_) { inline object getattr(handle obj, const char *name, handle default_) {
if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) {
return {result, false}; return reinterpret_steal<object>(result);
} else { } else {
PyErr_Clear(); 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) && { std::move(*this).operator=(handle(o)); }
void operator=(const object &o) & { operator=(handle(o)); } void operator=(const object &o) & { operator=(handle(o)); }
void operator=(handle value) && { Policy::set(obj, key, value); } 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> template <typename T = Policy>
PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") 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) { static object get(handle obj, handle key) {
PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr());
if (!result) { throw error_already_set(); } if (!result) { throw error_already_set(); }
return {result, false}; return reinterpret_steal<object>(result);
} }
static void set(handle obj, handle key, handle val) { static void set(handle obj, handle key, handle val) {
@ -281,7 +299,7 @@ struct sequence_item {
static object get(handle obj, size_t index) { static object get(handle obj, size_t index) {
PyObject *result = PySequence_GetItem(obj.ptr(), static_cast<ssize_t>(index)); PyObject *result = PySequence_GetItem(obj.ptr(), static_cast<ssize_t>(index));
if (!result) { throw error_already_set(); } if (!result) { throw error_already_set(); }
return {result, true}; return reinterpret_borrow<object>(result);
} }
static void set(handle obj, size_t index, handle val) { 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) { static object get(handle obj, size_t index) {
PyObject *result = PyList_GetItem(obj.ptr(), static_cast<ssize_t>(index)); PyObject *result = PyList_GetItem(obj.ptr(), static_cast<ssize_t>(index));
if (!result) { throw error_already_set(); } if (!result) { throw error_already_set(); }
return {result, true}; return reinterpret_borrow<object>(result);
} }
static void set(handle obj, size_t index, handle val) { 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) { static object get(handle obj, size_t index) {
PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index)); PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index));
if (!result) { throw error_already_set(); } if (!result) { throw error_already_set(); }
return {result, true}; return reinterpret_borrow<object>(result);
} }
static void set(handle obj, size_t index, handle val) { static void set(handle obj, size_t index, handle val) {
@ -390,23 +408,30 @@ class unpacking_collector;
NAMESPACE_END(detail) 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) \ #define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
public: \ 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") \ PYBIND11_DEPRECATED("Use py::isinstance<py::python_type>(obj) instead") \
bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \
static bool _check(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } static bool _check(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); }
#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ #define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ 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: */ \ /* 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) \ #define PYBIND11_OBJECT(Name, Parent, CheckFun) \
PYBIND11_OBJECT_COMMON(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: */ \ /* 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) \ #define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \
PYBIND11_OBJECT(Name, Parent, CheckFun) \ PYBIND11_OBJECT(Name, Parent, CheckFun) \
@ -448,7 +473,7 @@ public:
} }
private: private:
void advance() { value = object(PyIter_Next(m_ptr), false); } void advance() { value = reinterpret_steal<object>(PyIter_Next(m_ptr)); }
private: private:
object value = {}; object value = {};
@ -467,13 +492,13 @@ public:
PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str)
str(const char *c, size_t n) 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!"); 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 // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects
str(const char *c = "") str(const char *c = "")
: object(PyUnicode_FromString(c), false) { : object(PyUnicode_FromString(c), stolen) {
if (!m_ptr) pybind11_fail("Could not allocate string object!"); if (!m_ptr) pybind11_fail("Could not allocate string object!");
} }
@ -481,12 +506,12 @@ public:
explicit str(const bytes &b); 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 { operator std::string() const {
object temp = *this; object temp = *this;
if (PyUnicode_Check(m_ptr)) { if (PyUnicode_Check(m_ptr)) {
temp = object(PyUnicode_AsUTF8String(m_ptr), false); temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(m_ptr));
if (!temp) if (!temp)
pybind11_fail("Unable to extract string contents! (encoding issue)"); pybind11_fail("Unable to extract string contents! (encoding issue)");
} }
@ -526,12 +551,12 @@ public:
// Allow implicit conversion: // Allow implicit conversion:
bytes(const char *c = "") 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!"); if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
} }
bytes(const char *c, size_t n) 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!"); if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
} }
@ -552,7 +577,7 @@ public:
inline bytes::bytes(const pybind11::str &s) { inline bytes::bytes(const pybind11::str &s) {
object temp = s; object temp = s;
if (PyUnicode_Check(s.ptr())) { if (PyUnicode_Check(s.ptr())) {
temp = object(PyUnicode_AsUTF8String(s.ptr()), false); temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(s.ptr()));
if (!temp) if (!temp)
pybind11_fail("Unable to extract string contents! (encoding issue)"); pybind11_fail("Unable to extract string contents! (encoding issue)");
} }
@ -560,7 +585,7 @@ inline bytes::bytes(const pybind11::str &s) {
ssize_t length; ssize_t length;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
pybind11_fail("Unable to extract string contents! (invalid type)"); 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) if (!obj)
pybind11_fail("Could not allocate bytes object!"); pybind11_fail("Could not allocate bytes object!");
m_ptr = obj.release().ptr(); m_ptr = obj.release().ptr();
@ -571,7 +596,7 @@ inline str::str(const bytes& b) {
ssize_t length; ssize_t length;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length))
pybind11_fail("Unable to extract bytes contents!"); 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) if (!obj)
pybind11_fail("Could not allocate string object!"); pybind11_fail("Could not allocate string object!");
m_ptr = obj.release().ptr(); m_ptr = obj.release().ptr();
@ -580,15 +605,15 @@ inline str::str(const bytes& b) {
class none : public object { class none : public object {
public: public:
PYBIND11_OBJECT(none, object, detail::PyNone_Check) PYBIND11_OBJECT(none, object, detail::PyNone_Check)
none() : object(Py_None, true) { } none() : object(Py_None, borrowed) { }
}; };
class bool_ : public object { class bool_ : public object {
public: public:
PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) 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`: // 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; } operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; }
private: private:
@ -603,7 +628,7 @@ private:
class int_ : public object { class int_ : public object {
public: public:
PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) 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: // Allow implicit conversion from C++ integral types:
template <typename T, template <typename T,
detail::enable_if_t<std::is_integral<T>::value, int> = 0> detail::enable_if_t<std::is_integral<T>::value, int> = 0>
@ -643,10 +668,10 @@ class float_ : public object {
public: public:
PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float)
// Allow implicit conversion from float/double: // 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!"); 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!"); if (!m_ptr) pybind11_fail("Could not allocate float object!");
} }
operator float() const { return (float) PyFloat_AsDouble(m_ptr); } operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
@ -656,7 +681,8 @@ public:
class weakref : public object { class weakref : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) 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!"); if (!m_ptr) pybind11_fail("Could not allocate weak reference!");
} }
}; };
@ -681,9 +707,10 @@ public:
class capsule : public object { class capsule : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) 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) 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!"); if (!m_ptr) pybind11_fail("Could not allocate capsule object!");
} }
template <typename T> operator T *() const { template <typename T> operator T *() const {
@ -696,7 +723,7 @@ public:
class tuple : public object { class tuple : public object {
public: public:
PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) 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!"); if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
} }
size_t size() const { return (size_t) PyTuple_Size(m_ptr); } size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
@ -706,7 +733,7 @@ public:
class dict : public object { class dict : public object {
public: public:
PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) 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!"); if (!m_ptr) pybind11_fail("Could not allocate dict object!");
} }
template <typename... Args, template <typename... Args,
@ -741,7 +768,7 @@ public:
class list : public object { class list : public object {
public: public:
PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) 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!"); if (!m_ptr) pybind11_fail("Could not allocate list object!");
} }
size_t size() const { return (size_t) PyList_Size(m_ptr); } 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 { class set : public object {
public: public:
PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) 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!"); if (!m_ptr) pybind11_fail("Could not allocate set object!");
} }
size_t size() const { return (size_t) PySet_Size(m_ptr); } 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; Py_XDECREF(str_value); str_value = unicode;
if (!str_value) throw error_already_set(); if (!str_value) throw error_already_set();
#endif #endif
return {str_value, false}; return reinterpret_steal<str>(str_value);
} }
NAMESPACE_BEGIN(detail) 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>::begin() const {
template <typename D> iterator object_api<D>::end() const { return {nullptr, false}; } return reinterpret_steal<iterator>(PyObject_GetIter(derived().ptr()));
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> iterator object_api<D>::end() const {
template <typename D> obj_attr_accessor object_api<D>::attr(handle key) const { return {derived(), object(key, true)}; } return {};
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> 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 { template <typename D> template <typename T> bool object_api<D>::contains(T &&key) const {
return attr("__contains__")(std::forward<T>(key)).template cast<bool>(); 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) { bool load(handle src, bool convert) {
if (!isinstance<pybind11::set>(src)) if (!isinstance<pybind11::set>(src))
return false; return false;
pybind11::set s(src, true); auto s = reinterpret_borrow<pybind11::set>(src);
value.clear(); value.clear();
key_conv conv; key_conv conv;
for (auto entry : s) { 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) { static handle cast(const type &src, return_value_policy policy, handle parent) {
pybind11::set s; pybind11::set s;
for (auto const &value: src) { 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_)) if (!value_ || !s.add(value_))
return handle(); return handle();
} }
@ -79,7 +79,7 @@ template <typename Type, typename Key, typename Value> struct map_caster {
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!isinstance<dict>(src)) if (!isinstance<dict>(src))
return false; return false;
dict d(src, true); auto d = reinterpret_borrow<dict>(src);
key_conv kconv; key_conv kconv;
value_conv vconv; value_conv vconv;
value.clear(); 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) { static handle cast(const type &src, return_value_policy policy, handle parent) {
dict d; dict d;
for (auto const &kv: src) { for (auto const &kv: src) {
object key = object(key_conv::cast(kv.first, policy, parent), false); auto key = reinterpret_steal<object>(key_conv::cast(kv.first, policy, parent));
object value = object(value_conv::cast(kv.second, policy, parent), false); auto value = reinterpret_steal<object>(value_conv::cast(kv.second, policy, parent));
if (!key || !value) if (!key || !value)
return handle(); return handle();
d[key] = value; d[key] = value;
@ -114,7 +114,7 @@ template <typename Type, typename Value> struct list_caster {
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!isinstance<sequence>(src)) if (!isinstance<sequence>(src))
return false; return false;
sequence s(src, true); auto s = reinterpret_borrow<sequence>(src);
value_conv conv; value_conv conv;
value.clear(); value.clear();
reserve_maybe(s, &value); reserve_maybe(s, &value);
@ -135,7 +135,7 @@ template <typename Type, typename Value> struct list_caster {
list l(src.size()); list l(src.size());
size_t index = 0; size_t index = 0;
for (auto const &value: src) { 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_) if (!value_)
return handle(); return handle();
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference 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) { bool load(handle src, bool convert) {
if (!isinstance<list>(src)) if (!isinstance<list>(src))
return false; return false;
list l(src, true); auto l = reinterpret_borrow<list>(src);
if (l.size() != Size) if (l.size() != Size)
return false; return false;
value_conv conv; value_conv conv;
@ -176,7 +176,7 @@ template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>>
list l(Size); list l(Size);
size_t index = 0; size_t index = 0;
for (auto const &value: src) { 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_) if (!value_)
return handle(); return handle();
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference