mirror of
https://github.com/pybind/pybind11.git
synced 2026-05-12 09:17:42 +00:00
improved handling of shared/smart pointers
Previously, pybind11 required classes using std::shared_ptr<> to derive from std::enable_shared_from_this<> (or compilation failures would ensue). Everything now also works for classes that don't do this, assuming that some basic rules are followed (e.g. never passing "raw" pointers of instances manged by shared pointers). The safer std::enable_shared_from_this<> approach continues to be supported.
This commit is contained in:
@@ -19,9 +19,9 @@
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
class type_caster_custom {
|
||||
class type_caster_generic {
|
||||
public:
|
||||
PYBIND11_NOINLINE type_caster_custom(const std::type_info *type_info) {
|
||||
PYBIND11_NOINLINE type_caster_generic(const std::type_info *type_info) {
|
||||
auto & registered_types = get_internals().registered_types;
|
||||
auto it = registered_types.find(type_info);
|
||||
if (it != registered_types.end()) {
|
||||
@@ -57,7 +57,9 @@ public:
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE static PyObject *cast(const void *_src, return_value_policy policy, PyObject *parent,
|
||||
const std::type_info *type_info, void *(*copy_constructor)(const void *)) {
|
||||
const std::type_info *type_info,
|
||||
void *(*copy_constructor)(const void *),
|
||||
const void *existing_holder = nullptr) {
|
||||
void *src = const_cast<void *>(_src);
|
||||
if (src == nullptr) {
|
||||
Py_INCREF(Py_None);
|
||||
@@ -100,7 +102,7 @@ public:
|
||||
Py_XINCREF(parent);
|
||||
}
|
||||
PyObject *inst_pyobj = (PyObject *) inst;
|
||||
reg_type.init_holder(inst_pyobj);
|
||||
reg_type.init_holder(inst_pyobj, existing_holder);
|
||||
if (!dont_cache)
|
||||
internals.registered_instances[inst->value] = inst_pyobj;
|
||||
return inst_pyobj;
|
||||
@@ -113,20 +115,20 @@ protected:
|
||||
};
|
||||
|
||||
/// Generic type caster for objects stored on the heap
|
||||
template <typename type, typename Enable = void> class type_caster : public type_caster_custom {
|
||||
template <typename type, typename Enable = void> class type_caster : public type_caster_generic {
|
||||
public:
|
||||
static PYBIND11_DESCR name() { return type_descr(_<type>()); }
|
||||
|
||||
type_caster() : type_caster_custom(&typeid(type)) { }
|
||||
type_caster() : type_caster_generic(&typeid(type)) { }
|
||||
|
||||
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
|
||||
if (policy == return_value_policy::automatic)
|
||||
policy = return_value_policy::copy;
|
||||
return type_caster_custom::cast(&src, policy, parent, &typeid(type), ©_constructor);
|
||||
return type_caster_generic::cast(&src, policy, parent, &typeid(type), ©_constructor);
|
||||
}
|
||||
|
||||
static PyObject *cast(const type *src, return_value_policy policy, PyObject *parent) {
|
||||
return type_caster_custom::cast(src, policy, parent, &typeid(type), ©_constructor);
|
||||
return type_caster_generic::cast(src, policy, parent, &typeid(type), ©_constructor);
|
||||
}
|
||||
|
||||
operator type*() { return (type *) value; }
|
||||
@@ -134,7 +136,7 @@ public:
|
||||
protected:
|
||||
template <typename T = type, typename std::enable_if<std::is_copy_constructible<T>::value, int>::type = 0>
|
||||
static void *copy_constructor(const void *arg) {
|
||||
return new type(*((const type *)arg));
|
||||
return new type(*((const type *) arg));
|
||||
}
|
||||
template <typename T = type, typename std::enable_if<!std::is_copy_constructible<T>::value, int>::type = 0>
|
||||
static void *copy_constructor(const void *) { return nullptr; }
|
||||
@@ -433,24 +435,29 @@ protected:
|
||||
/// Type caster for holder types like std::shared_ptr, etc.
|
||||
template <typename type, typename holder_type> class type_caster_holder : public type_caster<type> {
|
||||
public:
|
||||
typedef type_caster<type> parent;
|
||||
using type_caster<type>::cast;
|
||||
using type_caster<type>::typeinfo;
|
||||
using type_caster<type>::value;
|
||||
using type_caster<type>::temp;
|
||||
using type_caster<type>::copy_constructor;
|
||||
|
||||
template <typename T = holder_type,
|
||||
typename std::enable_if<std::is_same<std::shared_ptr<type>, T>::value, int>::type = 0>
|
||||
bool load(PyObject *src, bool convert) {
|
||||
if (!parent::load(src, convert))
|
||||
if (src == nullptr || typeinfo == nullptr)
|
||||
return false;
|
||||
holder = holder_type(((type *) parent::value)->shared_from_this());
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T = holder_type,
|
||||
typename std::enable_if<!std::is_same<std::shared_ptr<type>, T>::value, int>::type = 0>
|
||||
bool load(PyObject *src, bool convert) {
|
||||
if (!parent::load(src, convert))
|
||||
return false;
|
||||
holder = holder_type((type *) parent::value);
|
||||
return true;
|
||||
if (PyType_IsSubtype(Py_TYPE(src), typeinfo->type)) {
|
||||
auto inst = (instance<type, holder_type> *) src;
|
||||
value = inst->value;
|
||||
holder = inst->holder;
|
||||
return true;
|
||||
}
|
||||
if (convert) {
|
||||
for (auto &converter : typeinfo->implicit_conversions) {
|
||||
temp = object(converter(src, typeinfo->type), false);
|
||||
if (load(temp.ptr(), false))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
explicit operator type*() { return this->value; }
|
||||
@@ -458,9 +465,9 @@ public:
|
||||
explicit operator holder_type&() { return holder; }
|
||||
explicit operator holder_type*() { return &holder; }
|
||||
|
||||
using type_caster<type>::cast;
|
||||
static PyObject *cast(const holder_type &src, return_value_policy policy, PyObject *parent) {
|
||||
return type_caster<type>::cast(src.get(), policy, parent);
|
||||
return type_caster_generic::cast(
|
||||
src.get(), policy, parent, &typeid(type), ©_constructor, &src);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -177,7 +177,7 @@ template <typename type, typename holder_type = std::unique_ptr<type>> struct in
|
||||
struct type_info {
|
||||
PyTypeObject *type;
|
||||
size_t type_size;
|
||||
void (*init_holder)(PyObject *);
|
||||
void (*init_holder)(PyObject *, const void *);
|
||||
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions;
|
||||
buffer_info *(*get_buffer)(PyObject *, void *) = nullptr;
|
||||
void *get_buffer_data = nullptr;
|
||||
|
||||
@@ -122,32 +122,32 @@ private:
|
||||
template <typename... T> using arg_value_caster =
|
||||
detail::type_caster<typename std::tuple<T...>>;
|
||||
|
||||
template <typename... T> static void process_extras(const std::tuple<T...> &args, function_entry *entry) {
|
||||
process_extras(args, entry, typename detail::make_index_sequence<sizeof...(T)>::type());
|
||||
template <typename... T> static void process_static(const std::tuple<T...> &args, function_entry *entry) {
|
||||
process_static(args, entry, typename detail::make_index_sequence<sizeof...(T)>::type());
|
||||
}
|
||||
|
||||
template <typename... T, size_t ... Index> static void process_extras(const std::tuple<T...> &args,
|
||||
template <typename... T, size_t ... Index> static void process_static(const std::tuple<T...> &args,
|
||||
function_entry *entry, detail::index_sequence<Index...>) {
|
||||
int unused[] = { 0, (process_extra(std::get<Index>(args), entry), 0)... };
|
||||
int unused[] = { 0, (process_static(std::get<Index>(args), entry), 0)... };
|
||||
(void) unused;
|
||||
}
|
||||
|
||||
template <int Nurse, int Patient>
|
||||
static void process_extra(const keep_alive<Nurse, Patient> &, function_entry *) { }
|
||||
static void process_extra(const char *doc, function_entry *entry) { entry->doc = (char *) doc; }
|
||||
static void process_extra(const pybind11::doc &d, function_entry *entry) { entry->doc = (char *) d.value; }
|
||||
static void process_extra(const pybind11::name &n, function_entry *entry) { entry->name = (char *) n.value; }
|
||||
static void process_extra(const pybind11::return_value_policy p, function_entry *entry) { entry->policy = p; }
|
||||
static void process_extra(const pybind11::sibling s, function_entry *entry) { entry->sibling = s.value; }
|
||||
static void process_extra(const pybind11::is_method &m, function_entry *entry) { entry->class_ = m.class_; }
|
||||
static void process_extra(const pybind11::arg &a, function_entry *entry) {
|
||||
static void process_static(const keep_alive<Nurse, Patient> &, function_entry *) { }
|
||||
static void process_static(const char *doc, function_entry *entry) { entry->doc = (char *) doc; }
|
||||
static void process_static(const pybind11::doc &d, function_entry *entry) { entry->doc = (char *) d.value; }
|
||||
static void process_static(const pybind11::name &n, function_entry *entry) { entry->name = (char *) n.value; }
|
||||
static void process_static(const pybind11::return_value_policy p, function_entry *entry) { entry->policy = p; }
|
||||
static void process_static(const pybind11::sibling s, function_entry *entry) { entry->sibling = s.value; }
|
||||
static void process_static(const pybind11::is_method &m, function_entry *entry) { entry->class_ = m.class_; }
|
||||
static void process_static(const pybind11::arg &a, function_entry *entry) {
|
||||
if (entry->class_ && entry->args.empty())
|
||||
entry->args.emplace_back("self", nullptr, nullptr);
|
||||
entry->args.emplace_back(a.name, nullptr, nullptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void process_extra(const pybind11::arg_t<T> &a, function_entry *entry) {
|
||||
static void process_static(const pybind11::arg_t<T> &a, function_entry *entry) {
|
||||
if (entry->class_ && entry->args.empty())
|
||||
entry->args.emplace_back("self", nullptr, nullptr);
|
||||
|
||||
@@ -184,7 +184,7 @@ public:
|
||||
return result;
|
||||
};
|
||||
|
||||
process_extras(std::make_tuple(std::forward<Extra>(extra)...), m_entry);
|
||||
process_static(std::make_tuple(std::forward<Extra>(extra)...), m_entry);
|
||||
PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name();
|
||||
initialize(signature.text(), signature.types(), sizeof...(Args));
|
||||
}
|
||||
@@ -243,7 +243,7 @@ private:
|
||||
return result;
|
||||
};
|
||||
|
||||
process_extras(std::make_tuple(std::forward<Extra>(extra)...), m_entry);
|
||||
process_static(std::make_tuple(std::forward<Extra>(extra)...), m_entry);
|
||||
PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name();
|
||||
initialize(signature.text(), signature.types(), sizeof...(Args));
|
||||
}
|
||||
@@ -332,7 +332,7 @@ private:
|
||||
const detail::type_info *type_info =
|
||||
capsule(PyObject_GetAttrString((PyObject *) Py_TYPE(inst),
|
||||
const_cast<char *>("__pybind11__")), false);
|
||||
type_info->init_holder(inst);
|
||||
type_info->init_holder(inst, nullptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -396,7 +396,7 @@ private:
|
||||
}
|
||||
} else if (c == '%') {
|
||||
const std::type_info *t = types[type_index++];
|
||||
if (!t)
|
||||
if (!t)
|
||||
throw std::runtime_error("Internal error while parsing type signature (1)");
|
||||
auto it = registered_types.find(t);
|
||||
if (it != registered_types.end()) {
|
||||
@@ -558,7 +558,7 @@ public:
|
||||
|
||||
custom_type(object &scope, const char *name_, const std::type_info *tinfo,
|
||||
size_t type_size, size_t instance_size,
|
||||
void (*init_holder)(PyObject *), const destructor &dealloc,
|
||||
void (*init_holder)(PyObject *, const void *), const destructor &dealloc,
|
||||
PyObject *parent, const char *doc) {
|
||||
PyHeapTypeObject *type = (PyHeapTypeObject*) PyType_Type.tp_alloc(&PyType_Type, 0);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
@@ -884,24 +884,37 @@ public:
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
template <typename T = holder_type,
|
||||
typename std::enable_if<!std::is_same<std::shared_ptr<type>, T>::value, int>::type = 0>
|
||||
static void init_holder(PyObject *inst_) {
|
||||
instance_type *inst = (instance_type *) inst_;
|
||||
new (&inst->holder) holder_type(inst->value);
|
||||
inst->constructed = true;
|
||||
}
|
||||
|
||||
template <typename T = holder_type,
|
||||
typename std::enable_if<std::is_same<std::shared_ptr<type>, T>::value, int>::type = 0>
|
||||
static void init_holder(PyObject *inst_) {
|
||||
instance_type *inst = (instance_type *) inst_;
|
||||
/// Initialize holder object, variant 1: object derives from enable_shared_from_this
|
||||
template <typename T>
|
||||
static void init_holder_helper(instance_type *inst, const holder_type * /* unused */, const std::enable_shared_from_this<T> * /* dummy */) {
|
||||
try {
|
||||
new (&inst->holder) holder_type(
|
||||
inst->value->shared_from_this());
|
||||
new (&inst->holder) holder_type(inst->value->shared_from_this());
|
||||
} catch (const std::bad_weak_ptr &) {
|
||||
new (&inst->holder) holder_type(inst->value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize holder object, variant 2: try to construct from existing holder object, if possible
|
||||
template <typename T = holder_type,
|
||||
typename std::enable_if<std::is_copy_constructible<T>::value, int>::type = 0>
|
||||
static void init_holder_helper(instance_type *inst, const holder_type *holder_ptr, const void * /* dummy */) {
|
||||
if (holder_ptr)
|
||||
new (&inst->holder) holder_type(*holder_ptr);
|
||||
else
|
||||
new (&inst->holder) holder_type(inst->value);
|
||||
}
|
||||
|
||||
/// Initialize holder object, variant 3: holder is not copy constructible (e.g. unique_ptr), always initialize from raw pointer
|
||||
template <typename T = holder_type,
|
||||
typename std::enable_if<!std::is_copy_constructible<T>::value, int>::type = 0>
|
||||
static void init_holder_helper(instance_type *inst, const holder_type * /* unused */, const void * /* dummy */) {
|
||||
new (&inst->holder) holder_type(inst->value);
|
||||
}
|
||||
|
||||
/// Initialize holder object of an instance, possibly given a pointer to an existing holder
|
||||
static void init_holder(PyObject *inst_, const void *holder_ptr) {
|
||||
auto inst = (instance_type *) inst_;
|
||||
init_holder_helper(inst, (const holder_type *) holder_ptr, inst->value);
|
||||
inst->constructed = true;
|
||||
}
|
||||
|
||||
@@ -964,21 +977,21 @@ template <typename... Args> struct init {
|
||||
|
||||
PYBIND11_NOINLINE inline void keep_alive_impl(int Nurse, int Patient, PyObject *arg, PyObject *ret) {
|
||||
/* Clever approach based on weak references taken from Boost.Python */
|
||||
PyObject *nurse = Nurse > 0 ? PyTuple_GetItem(arg, Nurse - 1) : ret;
|
||||
PyObject *patient = Patient > 0 ? PyTuple_GetItem(arg, Patient - 1) : ret;
|
||||
handle nurse (Nurse > 0 ? PyTuple_GetItem(arg, Nurse - 1) : ret);
|
||||
handle patient(Patient > 0 ? PyTuple_GetItem(arg, Patient - 1) : ret);
|
||||
|
||||
if (nurse == nullptr || patient == nullptr)
|
||||
throw std::runtime_error("Could not activate keep_alive");
|
||||
if (!nurse || !patient)
|
||||
throw std::runtime_error("Could not activate keep_alive!");
|
||||
|
||||
cpp_function disable_lifesupport(
|
||||
[patient](handle weakref) { Py_DECREF(patient); weakref.dec_ref(); }
|
||||
);
|
||||
[patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); });
|
||||
|
||||
PyObject *weakref = PyWeakref_NewRef(nurse, disable_lifesupport.ptr());
|
||||
if (weakref == nullptr)
|
||||
weakref wr(nurse, disable_lifesupport);
|
||||
if (!wr)
|
||||
throw std::runtime_error("Could not allocate weak reference!");
|
||||
|
||||
Py_INCREF(patient); /* reference patient and leak the weak reference */
|
||||
patient.inc_ref(); /* reference patient and leak the weak reference */
|
||||
(void) wr.release();
|
||||
}
|
||||
|
||||
template <int Nurse, int Patient> struct process_dynamic<keep_alive<Nurse, Patient>> : public process_dynamic<void> {
|
||||
|
||||
@@ -334,6 +334,12 @@ public:
|
||||
operator double() const { return (double) PyFloat_AsDouble(m_ptr); }
|
||||
};
|
||||
|
||||
class weakref : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check)
|
||||
weakref(handle obj, handle callback = handle()) : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), false) { }
|
||||
};
|
||||
|
||||
class slice : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check)
|
||||
|
||||
Reference in New Issue
Block a user