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:
Wenzel Jakob
2016-01-17 22:36:40 +01:00
parent 2ca07de83c
commit b2c2c79240
8 changed files with 493 additions and 397 deletions

View File

@@ -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), &copy_constructor);
return type_caster_generic::cast(&src, policy, parent, &typeid(type), &copy_constructor);
}
static PyObject *cast(const type *src, return_value_policy policy, PyObject *parent) {
return type_caster_custom::cast(src, policy, parent, &typeid(type), &copy_constructor);
return type_caster_generic::cast(src, policy, parent, &typeid(type), &copy_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), &copy_constructor, &src);
}
protected:

View File

@@ -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;

View File

@@ -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> {

View File

@@ -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)