mirror of
https://github.com/pybind/pybind11.git
synced 2025-02-20 07:30:40 +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:
|
.. _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
|
Module-local class bindings
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
@ -80,6 +80,59 @@ 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() { 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) {
|
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 +391,19 @@ 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::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.g == 7
|
||||||
|
|
||||||
assert o.get_g_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