Deprecated borrowed/stolen for borrowed_t{}/stolen_t{} (#771)

The constexpr static instances can cause linking failures if the
compiler doesn't optimize away the reference, as reported in #770.

There's no particularly nice way of fixing this in C++11/14: we can't
inline definitions to match the declaration aren't permitted for
non-templated static variables (C++17 *does* allows "inline" on
variables, but that obviously doesn't help us.)

One solution that could work around it is to add an extra inherited
subclass to `object`'s hierarchy, but that's a bit of a messy solution
and was decided against in #771 in favour of just deprecating (and
eventually dropping) the constexpr statics.

Fixes #770.
This commit is contained in:
Jason Rhinelander 2017-03-28 19:23:37 -03:00 committed by Wenzel Jakob
parent d6fdafb203
commit 6db60cd945
2 changed files with 36 additions and 31 deletions

View File

@ -714,16 +714,16 @@ public:
using value_type = T; using value_type = T;
array_t() : array(0, static_cast<const T *>(nullptr)) {} array_t() : array(0, static_cast<const T *>(nullptr)) {}
array_t(handle h, borrowed_t) : array(h, borrowed) { } array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { }
array_t(handle h, stolen_t) : array(h, stolen) { } array_t(handle h, stolen_t) : array(h, stolen_t{}) { }
PYBIND11_DEPRECATED("Use array_t<T>::ensure() instead") PYBIND11_DEPRECATED("Use array_t<T>::ensure() instead")
array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen) { array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) {
if (!m_ptr) PyErr_Clear(); if (!m_ptr) PyErr_Clear();
if (!is_borrowed) Py_XDECREF(h.ptr()); if (!is_borrowed) Py_XDECREF(h.ptr());
} }
array_t(const object &o) : array(raw_array_t(o.ptr()), stolen) { array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) {
if (!m_ptr) throw error_already_set(); if (!m_ptr) throw error_already_set();
} }

View File

@ -234,8 +234,13 @@ public:
protected: protected:
// Tags for choosing constructors from raw PyObject * // Tags for choosing constructors from raw PyObject *
struct borrowed_t { }; static constexpr borrowed_t borrowed{}; struct borrowed_t { };
struct stolen_t { }; static constexpr stolen_t stolen{}; struct stolen_t { };
// These can cause linkage problems; see #770
PYBIND11_DEPRECATED("Use of the `borrowed` static variable is deprecated; use `borrowed_t{}' instead")
static constexpr borrowed_t borrowed{};
PYBIND11_DEPRECATED("Use of the `stolen` static variable is deprecated; use `stolen_t{}' instead")
static constexpr stolen_t stolen{};
template <typename T> friend T reinterpret_borrow(handle); template <typename T> friend T reinterpret_borrow(handle);
template <typename T> friend T reinterpret_steal(handle); template <typename T> friend T reinterpret_steal(handle);
@ -259,7 +264,7 @@ public:
// or // or
py::tuple t = reinterpret_borrow<py::tuple>(p); // <-- `p` must be already be a `tuple` py::tuple t = reinterpret_borrow<py::tuple>(p); // <-- `p` must be already be a `tuple`
\endrst */ \endrst */
template <typename T> T reinterpret_borrow(handle h) { return {h, object::borrowed}; } template <typename T> T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; }
/** \rst /** \rst
Like `reinterpret_borrow`, but steals the reference. Like `reinterpret_borrow`, but steals the reference.
@ -269,7 +274,7 @@ template <typename T> T reinterpret_borrow(handle h) { return {h, object::borrow
PyObject *p = PyObject_Str(obj); PyObject *p = PyObject_Str(obj);
py::str s = reinterpret_steal<py::str>(p); // <-- `p` must be already be a `str` py::str s = reinterpret_steal<py::str>(p); // <-- `p` must be already be a `str`
\endrst */ \endrst */
template <typename T> T reinterpret_steal(handle h) { return {h, object::stolen}; } template <typename T> T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; }
/** \defgroup python_builtins _ /** \defgroup python_builtins _
Unless stated otherwise, the following C++ functions behave the same Unless stated otherwise, the following C++ functions behave the same
@ -674,9 +679,9 @@ NAMESPACE_END(detail)
#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">()") \ 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, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \
Name(handle h, borrowed_t) : Parent(h, borrowed) { } \ Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \
Name(handle h, stolen_t) : Parent(h, stolen) { } \ Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \
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()); }
@ -684,7 +689,7 @@ NAMESPACE_END(detail)
#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) \
/* 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()), stolen) { if (!m_ptr) throw error_already_set(); } Name(const object &o) : Parent(ConvertFun(o.ptr()), stolen_t{}) { 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) \
@ -778,13 +783,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), stolen) { : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) {
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), stolen) { : object(PyUnicode_FromString(c), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate string object!"); if (!m_ptr) pybind11_fail("Could not allocate string object!");
} }
@ -796,7 +801,7 @@ public:
Return a string representation of the object. This is analogous to Return a string representation of the object. This is analogous to
the ``str()`` function in Python. the ``str()`` function in Python.
\endrst */ \endrst */
explicit str(handle h) : object(raw_str(h.ptr()), stolen) { } explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { }
operator std::string() const { operator std::string() const {
object temp = *this; object temp = *this;
@ -846,12 +851,12 @@ public:
// Allow implicit conversion: // Allow implicit conversion:
bytes(const char *c = "") bytes(const char *c = "")
: object(PYBIND11_BYTES_FROM_STRING(c), stolen) { : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) {
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), stolen) { : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
} }
@ -900,15 +905,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, borrowed) { } none() : object(Py_None, borrowed_t{}) { }
}; };
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, borrowed) { } bool_() : object(Py_False, borrowed_t{}) { }
// Allow implicit conversion from and to `bool`: // Allow implicit conversion from and to `bool`:
bool_(bool value) : object(value ? Py_True : Py_False, borrowed) { } bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { }
operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; }
private: private:
@ -923,7 +928,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), stolen) { } int_() : object(PyLong_FromLong(0), stolen_t{}) { }
// 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>
@ -963,10 +968,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), stolen) { float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
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), stolen) { float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
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); }
@ -977,7 +982,7 @@ 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 = {}) explicit weakref(handle obj, handle callback = {})
: object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen) { : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); if (!m_ptr) pybind11_fail("Could not allocate weak reference!");
} }
}; };
@ -1003,17 +1008,17 @@ class capsule : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
PYBIND11_DEPRECATED("Use reinterpret_borrow<capsule>() or reinterpret_steal<capsule>()") 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)) { } capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { }
explicit capsule(const void *value) explicit capsule(const void *value)
: object(PyCapsule_New(const_cast<void *>(value), nullptr, nullptr), stolen) { : object(PyCapsule_New(const_cast<void *>(value), nullptr, nullptr), stolen_t{}) {
if (!m_ptr) if (!m_ptr)
pybind11_fail("Could not allocate capsule object!"); pybind11_fail("Could not allocate capsule object!");
} }
PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input")
capsule(const void *value, void (*destruct)(PyObject *)) capsule(const void *value, void (*destruct)(PyObject *))
: object(PyCapsule_New(const_cast<void*>(value), nullptr, destruct), stolen) { : object(PyCapsule_New(const_cast<void*>(value), nullptr, destruct), stolen_t{}) {
if (!m_ptr) if (!m_ptr)
pybind11_fail("Could not allocate capsule object!"); pybind11_fail("Could not allocate capsule object!");
} }
@ -1052,7 +1057,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), stolen) { explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) {
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); }
@ -1064,7 +1069,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(), stolen) { dict() : object(PyDict_New(), stolen_t{}) {
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,
@ -1101,7 +1106,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), stolen) { explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) {
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); }
@ -1119,7 +1124,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), stolen) { set() : object(PySet_New(nullptr), stolen_t{}) {
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); }