mirror of
https://github.com/pybind/pybind11.git
synced 2025-02-17 14:10:45 +00:00
Extend trampoline class pattern with example for nesting trampoline class hierarchies
This commit is contained in:
parent
3cc7e4258c
commit
b3c9615d2d
@ -903,6 +903,64 @@ are listed.
|
||||
|
||||
.. _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.
|
||||
|
||||
Module-local class bindings
|
||||
===========================
|
||||
|
||||
|
@ -80,6 +80,59 @@ struct I801D : I801C {}; // Indirect MI
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace TrampolineNesting {
|
||||
class ChainBaseA {
|
||||
public:
|
||||
ChainBaseA() = default;
|
||||
ChainBaseA(const ChainBaseA &) = default;
|
||||
ChainBaseA(ChainBaseA &&) = default;
|
||||
virtual ~ChainBaseA() = default;
|
||||
virtual int resultA() { return 1; }
|
||||
};
|
||||
class ChainChildA : public ChainBaseA {
|
||||
public:
|
||||
using ChainBaseA::ChainBaseA;
|
||||
int resultA() override { return 2; }
|
||||
};
|
||||
class ChainBaseB {
|
||||
public:
|
||||
ChainBaseB() = default;
|
||||
ChainBaseB(const ChainBaseB &) = default;
|
||||
ChainBaseB(ChainBaseB &&) = default;
|
||||
virtual ~ChainBaseB() = default;
|
||||
virtual std::string resultB() { return "A"; }
|
||||
};
|
||||
class ChainChildB : public ChainBaseB {
|
||||
public:
|
||||
using ChainBaseB::ChainBaseB;
|
||||
std::string resultB() override { return "B"; }
|
||||
};
|
||||
class Joined : public ChainChildA, public ChainChildB {
|
||||
public:
|
||||
Joined() = default;
|
||||
Joined(const Joined &) = default;
|
||||
Joined(Joined &&) = default;
|
||||
};
|
||||
|
||||
template <class Base = ChainBaseA>
|
||||
class TrampolineA : public Base {
|
||||
public:
|
||||
using Base::Base;
|
||||
int resultA() override { PYBIND11_OVERLOAD(int, Base, resultA, ); }
|
||||
};
|
||||
template <class Base = ChainBaseB, class PyBase = Base>
|
||||
class TrampolineB : public PyBase {
|
||||
public:
|
||||
using PyBase::PyBase;
|
||||
std::string resultB() override { PYBIND11_OVERLOAD(std::string, Base, resultB, ); }
|
||||
};
|
||||
template <class Base = Joined>
|
||||
class TrampolineJoined : public TrampolineB<Base, TrampolineA<Base>> {
|
||||
public:
|
||||
using TrampolineB<Base, TrampolineA<Base>>::TrampolineB;
|
||||
};
|
||||
} // namespace TrampolineNesting
|
||||
|
||||
TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
// Please do not interleave `struct` and `class` definitions with bindings code,
|
||||
// but implement `struct`s and `class`es in the anonymous namespace above.
|
||||
@ -338,4 +391,19 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
.def("get_f_e", &MVF::get_f_e)
|
||||
.def("get_f_f", &MVF::get_f_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::ChainBaseA, TN::ChainBaseB, TN::TrampolineJoined<>>(m, "Joined")
|
||||
.def(py::init<>());
|
||||
}
|
||||
|
@ -491,3 +491,12 @@ def test_python_inherit_from_mi():
|
||||
assert o.g == 7
|
||||
|
||||
assert o.get_g_g() == 7
|
||||
|
||||
|
||||
def test_trampoline_nesting():
|
||||
assert m.ChainBaseA().resultA() == 1
|
||||
assert m.ChainChildA().resultA() == 2
|
||||
assert m.ChainBaseB().resultB() == "A"
|
||||
assert m.ChainChildB().resultB() == "B"
|
||||
assert m.Joined().resultA() == 2
|
||||
assert m.Joined().resultB() == "B"
|
||||
|
Loading…
Reference in New Issue
Block a user