Support std::shared_ptr holder type out of the box

With this there is no more need for manual user declarations like
`PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)`. Existing ones
will still compile without error -- they will just be ignored silently.

Resolves #446.
This commit is contained in:
Dean Moldovan 2016-10-18 13:56:33 +02:00
parent f0b0df58a9
commit 5d28dd1194
8 changed files with 42 additions and 36 deletions

View File

@ -1,7 +1,7 @@
Smart pointers
##############
Unique pointers
std::unique_ptr
===============
Given a class ``Example`` with Python bindings, it's possible to return
@ -28,14 +28,8 @@ The above signature would imply that Python needs to give up ownership of an
object that is passed to this function, which is generally not possible (for
instance, the object might be referenced elsewhere).
.. _smart_pointers:
Reference-counting pointers
===========================
This section explains how to pass values that are wrapped in "smart" pointer
types with internal reference counting. For the simpler C++11 unique pointers,
refer to the previous section.
std::shared_ptr
===============
The binding generator for classes, :class:`class_`, can be passed a template
type that denotes a special *holder* type that is used to manage references to
@ -53,22 +47,6 @@ following snippet causes ``std::shared_ptr`` to be used instead.
Note that any particular class can only be associated with a single holder type.
To enable transparent conversions for functions that take shared pointers as an
argument or that return them, a macro invocation similar to the following must
be declared at the top level before any binding code:
.. code-block:: cpp
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
.. note::
The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a
placeholder name that is used as a template parameter of the second
argument. Thus, feel free to use any identifier, but use it consistently on
both sides; also, don't use the name of a type that already exists in your
codebase.
One potential stumbling block when using holder types is that they need to be
applied consistently. Can you guess what's broken about the following binding
code?
@ -139,6 +117,24 @@ There are two ways to resolve this issue:
class Child : public std::enable_shared_from_this<Child> { };
.. _smart_pointers:
Custom smart pointers
=====================
pybind11 supports ``std::unique_ptr`` and ``std::shared_ptr`` right out of the
box. For any other custom smart pointer, transparent conversions can be enabled
using a macro invocation similar to the following. It must be declared at the
level before any binding code:
.. code-block:: cpp
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>);
The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a
placeholder name that is used as a template parameter of the second argument.
Thus, feel free to use any identifier, but use it consistently on both sides;
also, don't use the name of a type that already exists in your codebase.
Please take a look at the :ref:`macro_notes` before using this feature.

View File

@ -55,6 +55,8 @@ Breaking changes queued for v2.0.0 (Not yet released)
``py::str s = "1 + 2 = {}"_s.format(3);``
* Attribute and item accessors now have a more complete interface which makes it possible
to chain attributes ``obj.attr("a")[key].attr("b").attr("method")(1, 2, 3)```.
* Added built-in support for ``std::shared_ptr`` holder type. There is no more need
to do it manually via ``PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)``.
* Various minor improvements of library internals (no user-visible changes)
1.8.1 (July 12, 2016)

View File

@ -383,12 +383,6 @@ public:
operator type&() { return value; } \
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>
#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type) \
namespace pybind11 { namespace detail { \
template <typename type> class type_caster<holder_type> \
: public type_caster_holder<type, holder_type> { }; \
}}
template <typename T>
struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value>> {
@ -898,6 +892,18 @@ protected:
holder_type holder;
};
/// Specialize for the common std::shared_ptr, so users don't need to
template <typename T>
class type_caster<std::shared_ptr<T>> : public type_caster_holder<T, std::shared_ptr<T>> { };
/// Create a specialization for custom holder types (silently ignores std::shared_ptr)
#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type) \
namespace pybind11 { namespace detail { \
template <typename type> \
class type_caster<holder_type, enable_if_t<!is_shared_ptr<holder_type>::value>> \
: public type_caster_holder<type, holder_type> { }; \
}}
// PYBIND11_DECLARE_HOLDER_TYPE holder types:
template <typename base, typename holder> struct is_holder_type :
std::is_base_of<detail::type_caster_holder<base, holder>, detail::type_caster<holder>> {};

View File

@ -417,6 +417,10 @@ using is_template_base_of = decltype(is_template_base_of_impl<Base>::check((T*)n
struct is_template_base_of : decltype(is_template_base_of_impl<Base>::check((T*)nullptr)) { };
#endif
/// Check if T is std::shared_ptr<U> where U can be anything
template <typename T> struct is_shared_ptr : std::false_type { };
template <typename U> struct is_shared_ptr<std::shared_ptr<U>> : std::true_type { };
/// Ignore that a variable is unused in compiler warnings
inline void ignore_unused(const int *) { }

View File

@ -9,7 +9,6 @@
#include "pybind11_tests.h"
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
template <int N> class BreaksBase {};
template <int N> class BreaksTramp : public BreaksBase<N> {};

View File

@ -12,7 +12,6 @@
#include <pybind11/stl.h>
#include <pybind11/operators.h>
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
#define TRACKERS(CLASS) CLASS() { print_default_created(this); } ~CLASS() { print_destroyed(this); }
struct NestABase { int value = -2; TRACKERS(NestABase) };

View File

@ -10,7 +10,6 @@
#include "pybind11_tests.h"
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
struct Base1 {
Base1(int i) : i(i) { }

View File

@ -82,8 +82,9 @@ private:
};
/// Make pybind aware of the ref-counted wrapper type (s)
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>);
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>); // Required for custom holder type
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>); // Not required any more for std::shared_ptr,
// but it should compile without error
Object *make_object_1() { return new MyObject1(1); }
ref<Object> make_object_2() { return new MyObject1(2); }