Skip to content

Hotfix: Rebase PR #2 #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,10 @@ install:
${PYPY:+--extra-index-url https://imaginary.ca/trusty-pypi}
echo "done."

wget -q -O eigen.tar.gz https://bitbucket.org/eigen/eigen/get/3.3.3.tar.gz
tar xzf eigen.tar.gz
export CMAKE_INCLUDE_PATH="${CMAKE_INCLUDE_PATH:+$CMAKE_INCLUDE_PATH:}$PWD/eigen-eigen-67e894c6cd8f"
mkdir eigen
curl -fsSL https://bitbucket.org/eigen/eigen/get/3.3.4.tar.bz2 | \
tar --extract -j --directory=eigen --strip-components=1
export CMAKE_INCLUDE_PATH="${CMAKE_INCLUDE_PATH:+$CMAKE_INCLUDE_PATH:}$PWD/eigen"
fi
set +e
script:
Expand Down
12 changes: 11 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,18 @@ adhere to the following rules to make the process as smooth as possible:
* This project has a strong focus on providing general solutions using a
minimal amount of code, thus small pull requests are greatly preferred.

### License
### Licensing of contributions

pybind11 is provided under a BSD-style license that can be found in the
``LICENSE`` file. By using, distributing, or contributing to this project, you
agree to the terms and conditions of this license.

You are under no obligation whatsoever to provide any bug fixes, patches, or
upgrades to the features, functionality or performance of the source code
("Enhancements") to anyone; however, if you choose to make your Enhancements
available either publicly, or directly to the author of this software, without
imposing a separate written license agreement for such Enhancements, then you
hereby grant the following license: a non-exclusive, royalty-free perpetual
license to install, use, modify, prepare derivative works, incorporate into
other computer software, distribute, and sublicense such enhancements or
derivative works thereof, in binary and source code form.
11 changes: 2 additions & 9 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,5 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

You are under no obligation whatsoever to provide any bug fixes, patches, or
upgrades to the features, functionality or performance of the source code
("Enhancements") to anyone; however, if you choose to make your Enhancements
available either publicly, or directly to the author of this software, without
imposing a separate written license agreement for such Enhancements, then you
hereby grant the following license: a non-exclusive, royalty-free perpetual
license to install, use, modify, prepare derivative works, incorporate into
other computer software, distribute, and sublicense such enhancements or
derivative works thereof, in binary and source code form.
Please also refer to the file CONTRIBUTING.md, which clarifies licensing of
external contributions to this project including patches, pull requests, etc.
3 changes: 3 additions & 0 deletions docs/advanced/cast/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
| ``std::string_view``, | STL C++17 string views | :file:`pybind11/pybind11.h` |
| ``std::u16string_view``, etc. | | |
+------------------------------------+---------------------------+-------------------------------+
| ``std::unique_ptr<T>``, | STL (or custom) smart | :file:`pybind11/cast.h` |
| ``std::shared_ptr<T>``, etc. | pointers. | |
+------------------------------------+---------------------------+-------------------------------+
| ``std::pair<T1, T2>`` | Pair of two custom types | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+
| ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` |
Expand Down
147 changes: 133 additions & 14 deletions docs/advanced/classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ helper class that is defined as follows:

.. code-block:: cpp

class PyAnimal : public Animal {
class PyAnimal : public py::wrapper<Animal> {
public:
/* Inherit the constructors */
using Animal::Animal;
using py::wrapper<Animal>::wrapper;

/* Trampoline (need one for each virtual function) */
std::string go(int n_times) override {
Expand All @@ -90,6 +90,8 @@ function* slots, which defines the name of function in Python. This is required
when the C++ and Python versions of the
function have different names, e.g. ``operator()`` vs ``__call__``.

The base class ``py::wrapper<>`` is optional, but is recommended as it allows us to attach the lifetime of Python objects directly to C++ objects, explained in :ref:`virtual_inheritance_lifetime`.

The binding code also needs a few minor adaptations (highlighted):

.. code-block:: cpp
Expand Down Expand Up @@ -157,7 +159,7 @@ Here is an example:

class Dachschund(Dog):
def __init__(self, name):
Dog.__init__(self) # Without this, undefind behavior may occur if the C++ portions are referenced.
Dog.__init__(self) # Without this, undefined behavior may occur if the C++ portions are referenced.
self.name = name
def bark(self):
return "yap!"
Expand Down Expand Up @@ -232,15 +234,15 @@ override the ``name()`` method):

.. code-block:: cpp

class PyAnimal : public Animal {
class PyAnimal : public py::wrapper<Animal> {
public:
using Animal::Animal; // Inherit constructors
using py::wrapper<Animal>::wrapper; // Inherit constructors
std::string go(int n_times) override { PYBIND11_OVERLOAD_PURE(std::string, Animal, go, n_times); }
std::string name() override { PYBIND11_OVERLOAD(std::string, Animal, name, ); }
};
class PyDog : public Dog {
class PyDog : public py::wrapper<Dog> {
public:
using Dog::Dog; // Inherit constructors
using py::wrapper<Dog>::wrapper; // Inherit constructors
std::string go(int n_times) override { PYBIND11_OVERLOAD_PURE(std::string, Dog, go, n_times); }
std::string name() override { PYBIND11_OVERLOAD(std::string, Dog, name, ); }
std::string bark() override { PYBIND11_OVERLOAD(std::string, Dog, bark, ); }
Expand All @@ -260,24 +262,24 @@ declare or override any virtual methods itself:
.. code-block:: cpp

class Husky : public Dog {};
class PyHusky : public Husky {
class PyHusky : public py::wrapper<Husky> {
public:
using Husky::Husky; // Inherit constructors
using py::wrapper<Husky>::wrapper; // Inherit constructors
std::string go(int n_times) override { PYBIND11_OVERLOAD_PURE(std::string, Husky, go, n_times); }
std::string name() override { PYBIND11_OVERLOAD(std::string, Husky, name, ); }
std::string bark() override { PYBIND11_OVERLOAD(std::string, Husky, bark, ); }
};

There is, however, a technique that can be used to avoid this duplication
(which can be especially helpful for a base class with several virtual
methods). The technique involves using template trampoline classes, as
methods). The technique (the Curiously Recurring Template Pattern) involves using template trampoline classes, as
follows:

.. code-block:: cpp

template <class AnimalBase = Animal> class PyAnimal : public AnimalBase {
template <class AnimalBase = Animal> class PyAnimal : public py::wrapper<AnimalBase> {
public:
using AnimalBase::AnimalBase; // Inherit constructors
using py::wrapper<AnimalBase>::wrapper; // Inherit constructors
std::string go(int n_times) override { PYBIND11_OVERLOAD_PURE(std::string, AnimalBase, go, n_times); }
std::string name() override { PYBIND11_OVERLOAD(std::string, AnimalBase, name, ); }
};
Expand Down Expand Up @@ -997,5 +999,122 @@ described trampoline:

MSVC 2015 has a compiler bug (fixed in version 2017) which
requires a more explicit function binding in the form of
``.def("foo", static_cast<int (A::*)() const>(&Publicist::foo));``
where ``int (A::*)() const`` is the type of ``A::foo``.
.. ``.def("foo", static_cast<int (A::*)() const>(&Publicist::foo));``
.. where ``int (A::*)() const`` is the type of ``A::foo``.

.. _virtual_inheritance_lifetime:

Virtual Inheritance and Lifetime
================================

When an instance of a Python subclass of a ``pybind11``-bound C++ class is instantiated, there are effectively two "portions": the C++ portion of the base class's alias instance, and the Python portion (``__dict__``) of the derived class instance.
Generally, the lifetime of an instance of a Python subclass of a ``pybind11``-bound C++ class will not pose an issue as long as the instance is owned in Python - that is, you can call virtual methods from C++ or Python and have the correct behavior.

However, if this Python-constructed instance is passed to C++ such that there are no other Python references, then C++ must keep the Python portion of the instance alive until either (a) the C++ reference is destroyed via ``delete`` or (b) the object is passed back to Python. ``pybind11`` supports both cases, but **only** when (i) the class inherits from :class:`py::wrapper`, (ii) there is only single-inheritance in the bound C++ classes, and (iii) the holder type for the class is either :class:`std::shared_ptr` (suggested) or :class:`std::unique_ptr` (default).

.. seealso::

:ref:`holders` has more information regaring holders and how general ownership transfer should function.

When ``pybind11`` detects case (a), it will store a reference to the Python object in :class:`py::wrapper` using :class:`py::object`, such that if the instance is deleted by C++, then it will also release the Python object (via :func:`py::wrapper::~wrapper()`). The wrapper will have a unique reference to the Python object (as any other circumstance would trigger case (b)), so the Python object should be destroyed immediately upon the instance's destruction.
This will be a cyclic reference per Python's memory management, but this is not an issue as the memory is now managed via C++.

For :class:`std::shared_ptr`, this case is detected by placing a shim :func:`__del__` method on the Python subclass when ``pybind11`` detects an instance being created. This shim will check for case (a), and if it holds, will "resurrect" since it created a new reference using :class:`py::object`.

For :class:`std::unique_ptr`, this case is detected when calling `py::cast<unique_ptr<T>>`, which itself implies ownership transfer.

.. seealso::

See :ref:`unique_ptr_ownership` for information about how ownership can be transferred via a cast or argument involving ``unique_ptr<Type>``.

When ``pybind11`` detects case (b) (e.g. ``py::cast()`` is called to convert a C++ instance to `py::object`) and (a) has previously occurred, such that C++ manages the lifetime of the object, then :class:`py::wrapper` will release the Python reference to allow Python to manage the lifetime of the object.

.. note::

This mechanism will be generally robust against reference cycles in Python as this couples the two "portions"; however, it does **not** protect against reference cycles with :class:`std::shared_ptr`. You should take care and use :class:`std::weak_ref` or raw pointers (with care) when needed.

.. note::

There will a slight difference in destructor order if the complete instance is destroyed in C++ or in Python; however, this difference will only be a difference in ordering in when :func:`py::wrapper::~wrapper()` (and your alias destructor) is called in relation to :func:`__del__` for the subclass. For more information, see the documentation comments for :class:`py::wrapper`.

For this example, we will build upon the above code for ``Animal`` with alias ``PyAnimal``, and the Python subclass ``Cat``, but will introduce a situation where C++ may have sole ownership: a container. In this case, it will be ``Cage``, which can contain or release an animal.

.. note::

For lifetime, it is important to use a more Python-friendly holder, which in this case would be :class:`std::shared_ptr`, permitting an ease to share ownership.

.. code-block:: cpp

class Animal {
public:
virtual ~Animal() { }
virtual std::string go(int n_times) = 0;
};

class PyAnimal : public py::wrapper<Animal> {
public:
/* Inherit the constructors */
using py::wrapper<Animal>::wrapper;
std::string go(int n_times) override {
PYBIND11_OVERLOAD_PURE(std::string, Animal, go, n_times);
}
};

class Cage {
public:
void add(std::shared_ptr<Animal> animal) {
animal_ = animal;
}
std::shared_ptr<Animal> release() {
return std::move(animal_);
}
private:
std::shared_ptr<Animal> animal_;
};

And the following bindings:

.. code-block:: cpp

PYBIND11_MODULE(example, m) {
py::class_<Animal, PyAnimal, std::shared_ptr<Animal>> animal(m, "Animal");
animal
.def(py::init<>())
.def("go", &Animal::go);

py::class_<Cage, std::shared_ptr<Cage>> cage(m, "Cage")
.def(py::init<>())
.def("add", &Cage::add)
.def("release", &Cage::release);
}

With the following Python preface:

.. code-block:: pycon

>>> from examples import *
>>> class Cat(Animal):
... def go(self, n_times):
... return "meow! " * n_times
...
>>> cage = Cage()

Normally, if you keep the object alive in Python, then no additional instrumentation is necessary:

.. code-block:: pycon

>>> cat = Cat()
>>> c.add(cat) # This object lives in both Python and C++.
>>> c.release().go(2)
meow! meow!

However, if you pass an instance that Python later wishes to destroy, without :class:`py::wrapper`, we would get an error that ``go`` is not implented,
as the `Cat` portion would have been destroyed and no longer visible for the trampoline. With the wrapper, ``pybind11`` will intercept this event and keep the Python portion alive:

.. code-block:: pycon

>>> c.add(Cat())
>>> c.release().go(2)
meow! meow!

Note that both the C++ and Python portion of ``cat`` will be destroyed once ``cage`` is destroyed.
15 changes: 15 additions & 0 deletions docs/advanced/misc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,21 @@ avoids this issue involves weak reference with a cleanup callback:
// Create a weak reference with a cleanup callback and initially leak it
(void) py::weakref(m.attr("BaseClass"), cleanup_callback).release();

.. note::

PyPy (at least version 5.9) does not garbage collect objects when the
interpreter exits. An alternative approach (which also works on CPython) is to use
the :py:mod:`atexit` module [#f7]_, for example:

.. code-block:: cpp

auto atexit = py::module::import("atexit");
atexit.attr("register")(py::cpp_function([]() {
// perform cleanup here -- this function is called with the GIL held
}));

.. [#f7] https://docs.python.org/3/library/atexit.html


Generating documentation using Sphinx
=====================================
Expand Down
Loading