mirror of https://github.com/pybind/pybind11.git
Merge cbb0590ac7
into 1f8b4a7f1a
This commit is contained in:
commit
2bf5f5550a
|
@ -866,6 +866,8 @@ which should look as follows:
|
||||||
|
|
||||||
.. [#f5] https://docs.python.org/3/library/copy.html
|
.. [#f5] https://docs.python.org/3/library/copy.html
|
||||||
|
|
||||||
|
.. _multiple_inheritance:
|
||||||
|
|
||||||
Multiple Inheritance
|
Multiple Inheritance
|
||||||
====================
|
====================
|
||||||
|
|
||||||
|
@ -902,6 +904,121 @@ are listed.
|
||||||
|
|
||||||
.. _module_local:
|
.. _module_local:
|
||||||
|
|
||||||
|
For more complex multiple-inheritance class architectures with virtual methods, it might be necessary to combine
|
||||||
|
two different Trampoline class hierarchies. A templating trick can be used in this case to interleaf another
|
||||||
|
trampoline class (-hierarchy):
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
class Animal {
|
||||||
|
public:
|
||||||
|
virtual ~Animal() { }
|
||||||
|
virtual std::string go(int n_times) = 0;
|
||||||
|
};
|
||||||
|
template <class AnimalBase = Animal>
|
||||||
|
class PyAnimal : public AnimalBase {
|
||||||
|
public:
|
||||||
|
using AnimalBase::AnimalBase; // Inherit constructors
|
||||||
|
std::string go(int n_times) override { PYBIND11_OVERRIDE_PURE(std::string, AnimalBase, go, n_times); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Dog : public Animal {
|
||||||
|
public:
|
||||||
|
std::string go(int n_times) override;
|
||||||
|
};
|
||||||
|
template <class DogBase = Dog>
|
||||||
|
class PyDog : public PyAnimal<DogBase> {
|
||||||
|
public:
|
||||||
|
using PyAnimal<DogBase>::PyAnimal; // Inherit constructors
|
||||||
|
std::string go(int n_times) override { PYBIND11_OVERRIDE(std::string, DogBase, go, n_times); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Mutant {
|
||||||
|
public:
|
||||||
|
virtual ~Mutant() { }
|
||||||
|
virtual void transform();
|
||||||
|
};
|
||||||
|
template <class MutantBase = Mutant, class PyMutantBase = MutantBase>
|
||||||
|
class PyMutant : public PyMutantBase {
|
||||||
|
public:
|
||||||
|
using PyMutantBase::PyMutantBase; // Inherit constructors
|
||||||
|
void transform() override { PYBIND11_OVERRIDE_PURE(void, MutantBase, transform, ); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Chimera : public Dog, public Mutant {
|
||||||
|
public:
|
||||||
|
virtual ~Chimera() { }
|
||||||
|
};
|
||||||
|
template <class ChimeraBase = Chimera>
|
||||||
|
class PyChimera : public PyMutant<ChimeraBase, PyDog<ChimeraBase>> {
|
||||||
|
public:
|
||||||
|
using PyMutant<ChimeraBase, PyDog<ChimeraBase>>::PyMutant; // Inherit constructors
|
||||||
|
};
|
||||||
|
|
||||||
|
The class ``Chimera`` inherits from both ``Dog`` and ``Mutant`` both of which feature virtual methods and
|
||||||
|
trampoline classes for binding. However, the ``Mutant`` trampoline class uses a second template parameter
|
||||||
|
``PyMutantBase`` so it can be injected into the single inheritance structure required by a trampoline class.
|
||||||
|
In effect, this mechanism enforces that the actual class the trampolines are using is only inherited from once.
|
||||||
|
Since the trampolines only need to add their respective trampoline function registrations, the order of the
|
||||||
|
inheritance of the various trampoline classes does not matter.
|
||||||
|
|
||||||
|
If the base classes contain pure virtual methods, another pattern can be applied to reduce the amount of
|
||||||
|
trampoline code that needs writing. The cost is an additional ``std::same`` call for each pure-virtual
|
||||||
|
method using the macro ``PYBIND11_OVERRIDE_TEMPLATE``.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
class Animal {
|
||||||
|
public:
|
||||||
|
virtual ~Animal() { }
|
||||||
|
virtual std::string go(int n_times) = 0;
|
||||||
|
};
|
||||||
|
class Dog : public Animal {
|
||||||
|
public:
|
||||||
|
std::string go(int n_times) override;
|
||||||
|
};
|
||||||
|
template <class AnimalBase = Animal, class PureVirtualBase = Animal>
|
||||||
|
class PyAnimal : public AnimalBase {
|
||||||
|
public:
|
||||||
|
using AnimalBase::AnimalBase; // Inherit constructors
|
||||||
|
std::string go(int n_times) override { PYBIND11_OVERRIDE_TEMPLATE(PureVirtualBase, std::string, AnimalBase, go, n_times); }
|
||||||
|
};
|
||||||
|
using PyDog = PyAnimal<Dog>
|
||||||
|
|
||||||
|
class Mutant {
|
||||||
|
public:
|
||||||
|
virtual ~Mutant() { }
|
||||||
|
virtual void transform() = 0;
|
||||||
|
};
|
||||||
|
class XMen : public Mutant{
|
||||||
|
public:
|
||||||
|
virtual ~Mutant() { }
|
||||||
|
void transform() override;
|
||||||
|
};
|
||||||
|
template <class MutantBase = Mutant, class PyMutantBase = MutantBase, class PureVirtualBase = Mutant>
|
||||||
|
class PyMutant : public PyMutantBase {
|
||||||
|
public:
|
||||||
|
using PyMutantBase::PyMutantBase; // Inherit constructors
|
||||||
|
void transform() override { PYBIND11_OVERRIDE_TEMPLATE(PureVirtualBase, void, MutantBase, transform, ); }
|
||||||
|
};
|
||||||
|
using PyXMen = PyMutant<XMen>
|
||||||
|
|
||||||
|
class Chimera : public Dog, public Mutant {
|
||||||
|
public:
|
||||||
|
virtual ~Chimera() { }
|
||||||
|
};
|
||||||
|
template <class ChimeraBase = Chimera, class PyChimeraBase = ChimeraBase>
|
||||||
|
class PyChimera : public PyMutant<ChimeraBase, PyAnimal<ChimeraBase, PyChimeraBase>> {
|
||||||
|
public:
|
||||||
|
using PyMutant<ChimeraBase, PyAnimal<ChimeraBase, PyChimeraBase>>::PyMutant; // Inherit constructors
|
||||||
|
};
|
||||||
|
|
||||||
|
The first parameter of the :c:macro:`PYBIND11_OVERRIDE_TEMPLATE` is the base class containing
|
||||||
|
the pure virtual method. Together with the cname parameter, an ``std::same`` call is used to
|
||||||
|
invoke either :c:macro:`PYBIND11_OVERRIDE_PURE` or :c:macro:`PYBIND11_OVERRIDE`. A corresponding
|
||||||
|
:c:macro:`PYBIND11_OVERRIDE_TEMPLATE_NAME` implementation is also available. The template parameter
|
||||||
|
``PureVirtualBase`` can be used in case the pure virtual methods are not implemented in a child class.
|
||||||
|
|
||||||
Module-local class bindings
|
Module-local class bindings
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -2963,6 +2964,51 @@ function get_override(const T *this_ptr, const char *name) {
|
||||||
PYBIND11_OVERRIDE_PURE_NAME( \
|
PYBIND11_OVERRIDE_PURE_NAME( \
|
||||||
PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)
|
PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
Macro to wrap :c:macro:`PYBIND11_OVERRIDE_PURE_NAME` and :c:macro:`PYBIND11_OVERRIDE_PURE`
|
||||||
|
depending on the base class and cname parameter provided.
|
||||||
|
See :ref:`_multiple_inheritance` for more information.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
template<class AnimalBase = Animal>
|
||||||
|
class PyAnimal : public AnimalBase {
|
||||||
|
public:
|
||||||
|
// Inherit the constructors
|
||||||
|
using AnimalBase::AnimalBase;
|
||||||
|
|
||||||
|
// Trampoline (need one for each virtual function)
|
||||||
|
std::string go(int n_times) override {
|
||||||
|
PYBIND11_OVERRIDE_TEMPLATE(
|
||||||
|
Animal, // The base class containing the purely virtual implementation
|
||||||
|
std::string, // Return type (ret_type)
|
||||||
|
Dog, // Parent class (cname)
|
||||||
|
"_go", // Name of method in Python (name)
|
||||||
|
go, // Name of function in C++ (must match Python name) (fn)
|
||||||
|
n_times // Argument(s) (...)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
\endrst */
|
||||||
|
#define PYBIND11_OVERRIDE_TEMPLATE_NAME(base, ret_type, cname, name, fn, ...) \
|
||||||
|
if (std::is_same<base, cname>::value) { \
|
||||||
|
PYBIND11_OVERRIDE_PURE_NAME( \
|
||||||
|
PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, fn, __VA_ARGS__); \
|
||||||
|
} else { \
|
||||||
|
PYBIND11_OVERRIDE_NAME( \
|
||||||
|
PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, fn, __VA_ARGS__); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
Macro to wrap :c:macro:`PYBIND11_OVERRIDE_NAME` and :c:macro:`PYBIND11_OVERRIDE`
|
||||||
|
depending on the base class and cname parameter provided.
|
||||||
|
Uses :c:macro:`PYBIND11_OVERRIDE_TEMPLATE_NAME` under the hood.
|
||||||
|
See :ref:`_multiple_inheritance` for more information.
|
||||||
|
\endrst */
|
||||||
|
#define PYBIND11_OVERRIDE_TEMPLATE(base, ret_type, cname, fn, ...) \
|
||||||
|
PYBIND11_OVERRIDE_TEMPLATE_NAME( \
|
||||||
|
PYBIND11_TYPE(base), PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)
|
||||||
|
|
||||||
// Deprecated versions
|
// Deprecated versions
|
||||||
|
|
||||||
PYBIND11_DEPRECATED("get_type_overload has been deprecated")
|
PYBIND11_DEPRECATED("get_type_overload has been deprecated")
|
||||||
|
|
|
@ -80,6 +80,61 @@ struct I801D : I801C {}; // Indirect MI
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace TrampolineNesting {
|
||||||
|
class ChainBaseA {
|
||||||
|
public:
|
||||||
|
ChainBaseA() = default;
|
||||||
|
ChainBaseA(const ChainBaseA &) = default;
|
||||||
|
ChainBaseA(ChainBaseA &&) = default;
|
||||||
|
virtual ~ChainBaseA() = default;
|
||||||
|
virtual int resultA() = 0;
|
||||||
|
};
|
||||||
|
class ChainChildA : public ChainBaseA {
|
||||||
|
public:
|
||||||
|
using ChainBaseA::ChainBaseA;
|
||||||
|
int resultA() override { return 1; }
|
||||||
|
};
|
||||||
|
class ChainBaseB {
|
||||||
|
public:
|
||||||
|
ChainBaseB() = default;
|
||||||
|
ChainBaseB(const ChainBaseB &) = default;
|
||||||
|
ChainBaseB(ChainBaseB &&) = default;
|
||||||
|
virtual ~ChainBaseB() = default;
|
||||||
|
virtual std::string resultB() = 0;
|
||||||
|
};
|
||||||
|
class ChainChildB : public ChainBaseB {
|
||||||
|
public:
|
||||||
|
using ChainBaseB::ChainBaseB;
|
||||||
|
std::string resultB() override { return "A"; }
|
||||||
|
};
|
||||||
|
class Joined : public ChainChildA, public ChainChildB {
|
||||||
|
public:
|
||||||
|
Joined() = default;
|
||||||
|
Joined(const Joined &) = default;
|
||||||
|
Joined(Joined &&) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Base = ChainBaseA, typename PureVirtualBase = ChainBaseA>
|
||||||
|
class TrampolineA : public Base {
|
||||||
|
public:
|
||||||
|
using Base::Base;
|
||||||
|
int resultA() override { PYBIND11_OVERRIDE_TEMPLATE(PureVirtualBase, int, Base, resultA, ) }
|
||||||
|
};
|
||||||
|
template <class Base = ChainBaseB, class PyBase = Base, typename PureVirtualBase = ChainBaseB>
|
||||||
|
class TrampolineB : public PyBase {
|
||||||
|
public:
|
||||||
|
using PyBase::PyBase;
|
||||||
|
std::string resultB() override {
|
||||||
|
PYBIND11_OVERRIDE_TEMPLATE(PureVirtualBase, std::string, Base, resultB, )
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <class Base = Joined, class PyBase = Base>
|
||||||
|
class TrampolineJoined : public TrampolineB<Base, TrampolineA<Base, PyBase>> {
|
||||||
|
public:
|
||||||
|
using TrampolineB<Base, TrampolineA<Base, PyBase>>::TrampolineB;
|
||||||
|
};
|
||||||
|
} // namespace TrampolineNesting
|
||||||
|
|
||||||
TEST_SUBMODULE(multiple_inheritance, m) {
|
TEST_SUBMODULE(multiple_inheritance, m) {
|
||||||
// Please do not interleave `struct` and `class` definitions with bindings code,
|
// Please do not interleave `struct` and `class` definitions with bindings code,
|
||||||
// but implement `struct`s and `class`es in the anonymous namespace above.
|
// but implement `struct`s and `class`es in the anonymous namespace above.
|
||||||
|
@ -338,4 +393,24 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||||
.def("get_f_e", &MVF::get_f_e)
|
.def("get_f_e", &MVF::get_f_e)
|
||||||
.def("get_f_f", &MVF::get_f_f)
|
.def("get_f_f", &MVF::get_f_f)
|
||||||
.def_readwrite("f", &MVF::f);
|
.def_readwrite("f", &MVF::f);
|
||||||
|
|
||||||
|
namespace TN = TrampolineNesting;
|
||||||
|
py::class_<TN::ChainBaseA, TN::TrampolineA<>>(m, "ChainBaseA")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("resultA", &TN::ChainBaseA::resultA);
|
||||||
|
py::class_<TN::ChainChildA, TN::ChainBaseA, TN::TrampolineA<TN::ChainChildA>>(m, "ChainChildA")
|
||||||
|
.def(py::init<>());
|
||||||
|
py::class_<TN::ChainBaseB, TN::TrampolineB<>>(m, "ChainBaseB")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("resultB", &TN::ChainBaseB::resultB);
|
||||||
|
py::class_<TN::ChainChildB, TN::ChainBaseB, TN::TrampolineB<TN::ChainChildB>>(m, "ChainChildB")
|
||||||
|
.def(py::init<>());
|
||||||
|
py::class_<TN::Joined, TN::ChainChildA, TN::ChainChildB, TN::TrampolineJoined<>>(m, "Joined")
|
||||||
|
.def(py::init<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Needed for MSVC linker
|
||||||
|
namespace TrampolineNesting {
|
||||||
|
int ChainBaseA::resultA() { return 0; }
|
||||||
|
std::string ChainBaseB::resultB() { return ""; }
|
||||||
|
} // namespace TrampolineNesting
|
||||||
|
|
|
@ -493,3 +493,14 @@ def test_python_inherit_from_mi():
|
||||||
assert o.g == 7
|
assert o.g == 7
|
||||||
|
|
||||||
assert o.get_g_g() == 7
|
assert o.get_g_g() == 7
|
||||||
|
|
||||||
|
|
||||||
|
def test_trampoline_nesting():
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
m.ChainBaseA().resultA()
|
||||||
|
assert m.ChainChildA().resultA() == 1
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
m.ChainBaseB().resultB()
|
||||||
|
assert m.ChainChildB().resultB() == "A"
|
||||||
|
assert m.Joined().resultA() == 1
|
||||||
|
assert m.Joined().resultB() == "A"
|
||||||
|
|
Loading…
Reference in New Issue