diff --git a/tests/test_multiple_inheritance.cpp b/tests/test_multiple_inheritance.cpp index 2fbe11278..c52b991aa 100644 --- a/tests/test_multiple_inheritance.cpp +++ b/tests/test_multiple_inheritance.cpp @@ -11,47 +11,74 @@ #include "pybind11_tests.h" #include "constructor_stats.h" -struct Base1 { - Base1(int i) : i(i) { } - int foo() { return i; } - int i; -}; - -struct Base2 { - Base2(int i) : i(i) { } - int bar() { return i; } - int i; -}; - +// Many bases for testing that multiple inheritance from many classes (i.e. requiring extra +// space for holder constructed flags) works. template struct BaseN { BaseN(int i) : i(i) { } int i; }; -struct Base12 : Base1, Base2 { - Base12(int i, int j) : Base1(i), Base2(j) { } +// test_mi_static_properties +struct Vanilla { + std::string vanilla() { return "Vanilla"; }; }; - -struct MIType : Base12 { - MIType(int i, int j) : Base12(i, j) { } +struct WithStatic1 { + static std::string static_func1() { return "WithStatic1"; }; + static int static_value1; }; +struct WithStatic2 { + static std::string static_func2() { return "WithStatic2"; }; + static int static_value2; +}; +struct VanillaStaticMix1 : Vanilla, WithStatic1, WithStatic2 { + static std::string static_func() { return "VanillaStaticMix1"; } + static int static_value; +}; +struct VanillaStaticMix2 : WithStatic1, Vanilla, WithStatic2 { + static std::string static_func() { return "VanillaStaticMix2"; } + static int static_value; +}; +int WithStatic1::static_value1 = 1; +int WithStatic2::static_value2 = 2; +int VanillaStaticMix1::static_value = 12; +int VanillaStaticMix2::static_value = 12; -test_initializer multiple_inheritance([](py::module &m) { +TEST_SUBMODULE(multiple_inheritance, m) { + + // test_multiple_inheritance_mix1 + // test_multiple_inheritance_mix2 + struct Base1 { + Base1(int i) : i(i) { } + int foo() { return i; } + int i; + }; py::class_ b1(m, "Base1"); b1.def(py::init()) .def("foo", &Base1::foo); + struct Base2 { + Base2(int i) : i(i) { } + int bar() { return i; } + int i; + }; py::class_ b2(m, "Base2"); b2.def(py::init()) .def("bar", &Base2::bar); - py::class_(m, "Base12"); + // test_multiple_inheritance_cpp + struct Base12 : Base1, Base2 { + Base12(int i, int j) : Base1(i), Base2(j) { } + }; + struct MIType : Base12 { + MIType(int i, int j) : Base12(i, j) { } + }; + py::class_(m, "Base12"); py::class_(m, "MIType") .def(py::init()); - // Many bases for testing that multiple inheritance from many classes (i.e. requiring extra - // space for holder constructed flags) works. + + // test_multiple_inheritance_python_many_bases #define PYBIND11_BASEN(N) py::class_>(m, "BaseN" #N).def(py::init()).def("f" #N, [](BaseN &b) { return b.i + N; }) PYBIND11_BASEN( 1); PYBIND11_BASEN( 2); PYBIND11_BASEN( 3); PYBIND11_BASEN( 4); PYBIND11_BASEN( 5); PYBIND11_BASEN( 6); PYBIND11_BASEN( 7); PYBIND11_BASEN( 8); @@ -67,55 +94,50 @@ test_initializer multiple_inheritance([](py::module &m) { // }; // py::class_(m, "Base12v2", b1, b2) // .def(py::init()); -}); -/* Test the case where not all base classes are specified, - and where pybind11 requires the py::multiple_inheritance - flag to perform proper casting between types */ -struct Base1a { - Base1a(int i) : i(i) { } - int foo() { return i; } - int i; -}; - -struct Base2a { - Base2a(int i) : i(i) { } - int bar() { return i; } - int i; -}; - -struct Base12a : Base1a, Base2a { - Base12a(int i, int j) : Base1a(i), Base2a(j) { } -}; - -test_initializer multiple_inheritance_nonexplicit([](py::module &m) { + // test_multiple_inheritance_virtbase + // Test the case where not all base classes are specified, and where pybind11 requires the + // py::multiple_inheritance flag to perform proper casting between types. + struct Base1a { + Base1a(int i) : i(i) { } + int foo() { return i; } + int i; + }; py::class_>(m, "Base1a") .def(py::init()) .def("foo", &Base1a::foo); + struct Base2a { + Base2a(int i) : i(i) { } + int bar() { return i; } + int i; + }; py::class_>(m, "Base2a") .def(py::init()) .def("bar", &Base2a::bar); + struct Base12a : Base1a, Base2a { + Base12a(int i, int j) : Base1a(i), Base2a(j) { } + }; py::class_>(m, "Base12a", py::multiple_inheritance()) .def(py::init()); m.def("bar_base2a", [](Base2a *b) { return b->bar(); }); m.def("bar_base2a_sharedptr", [](std::shared_ptr b) { return b->bar(); }); -}); -// Issue #801: invalid casting to derived type with MI bases -struct I801B1 { int a = 1; virtual ~I801B1() = default; }; -struct I801B2 { int b = 2; virtual ~I801B2() = default; }; -struct I801C : I801B1, I801B2 {}; -struct I801D : I801C {}; // Indirect MI -// Unregistered classes: -struct I801B3 { int c = 3; virtual ~I801B3() = default; }; -struct I801E : I801B3, I801D {}; + // test_mi_unaligned_base + // test_mi_base_return + // Issue #801: invalid casting to derived type with MI bases + struct I801B1 { int a = 1; virtual ~I801B1() = default; }; + struct I801B2 { int b = 2; virtual ~I801B2() = default; }; + struct I801C : I801B1, I801B2 {}; + struct I801D : I801C {}; // Indirect MI + // Unregistered classes: + struct I801B3 { int c = 3; virtual ~I801B3() = default; }; + struct I801E : I801B3, I801D {}; -test_initializer multiple_inheritance_casting([](py::module &m) { py::class_>(m, "I801B1").def(py::init<>()).def_readonly("a", &I801B1::a); py::class_>(m, "I801B2").def(py::init<>()).def_readonly("b", &I801B2::b); py::class_>(m, "I801C").def(py::init<>()); @@ -141,46 +163,9 @@ test_initializer multiple_inheritance_casting([](py::module &m) { // isn't pybind-registered (and uses multiple-inheritance to offset the pybind base) m.def("i801e_c", []() -> I801C * { return new I801E(); }); m.def("i801e_b2", []() -> I801B2 * { return new I801E(); }); -}); -struct Vanilla { - std::string vanilla() { return "Vanilla"; }; -}; - -struct WithStatic1 { - static std::string static_func1() { return "WithStatic1"; }; - static int static_value1; -}; - -struct WithStatic2 { - static std::string static_func2() { return "WithStatic2"; }; - static int static_value2; -}; - -struct WithDict { }; - -struct VanillaStaticMix1 : Vanilla, WithStatic1, WithStatic2 { - static std::string static_func() { return "VanillaStaticMix1"; } - static int static_value; -}; - -struct VanillaStaticMix2 : WithStatic1, Vanilla, WithStatic2 { - static std::string static_func() { return "VanillaStaticMix2"; } - static int static_value; -}; - -struct VanillaDictMix1 : Vanilla, WithDict { }; -struct VanillaDictMix2 : WithDict, Vanilla { }; - -int WithStatic1::static_value1 = 1; -int WithStatic2::static_value2 = 2; -int VanillaStaticMix1::static_value = 12; -int VanillaStaticMix2::static_value = 12; - -test_initializer mi_static_properties([](py::module &pm) { - auto m = pm.def_submodule("mi"); - + // test_mi_static_properties py::class_(m, "Vanilla") .def(py::init<>()) .def("vanilla", &Vanilla::vanilla); @@ -207,9 +192,13 @@ test_initializer mi_static_properties([](py::module &pm) { .def_static("static_func", &VanillaStaticMix2::static_func) .def_readwrite_static("static_value", &VanillaStaticMix2::static_value); + #if !defined(PYPY_VERSION) + struct WithDict { }; + struct VanillaDictMix1 : Vanilla, WithDict { }; + struct VanillaDictMix2 : WithDict, Vanilla { }; py::class_(m, "WithDict", py::dynamic_attr()).def(py::init<>()); py::class_(m, "VanillaDictMix1").def(py::init<>()); py::class_(m, "VanillaDictMix2").def(py::init<>()); #endif -}); +} diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index 3ed47adcc..7121c81ab 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -1,19 +1,16 @@ import pytest from pybind11_tests import ConstructorStats +from pybind11_tests import multiple_inheritance as m def test_multiple_inheritance_cpp(): - from pybind11_tests import MIType - - mt = MIType(3, 4) + mt = m.MIType(3, 4) assert mt.foo() == 3 assert mt.bar() == 4 def test_multiple_inheritance_mix1(): - from pybind11_tests import Base2 - class Base1: def __init__(self, i): self.i = i @@ -21,10 +18,10 @@ def test_multiple_inheritance_mix1(): def foo(self): return self.i - class MITypePy(Base1, Base2): + class MITypePy(Base1, m.Base2): def __init__(self, i, j): Base1.__init__(self, i) - Base2.__init__(self, j) + m.Base2.__init__(self, j) mt = MITypePy(3, 4) @@ -33,7 +30,6 @@ def test_multiple_inheritance_mix1(): def test_multiple_inheritance_mix2(): - from pybind11_tests import Base1 class Base2: def __init__(self, i): @@ -42,9 +38,9 @@ def test_multiple_inheritance_mix2(): def bar(self): return self.i - class MITypePy(Base1, Base2): + class MITypePy(m.Base1, Base2): def __init__(self, i, j): - Base1.__init__(self, i) + m.Base1.__init__(self, i) Base2.__init__(self, j) mt = MITypePy(3, 4) @@ -54,41 +50,40 @@ def test_multiple_inheritance_mix2(): def test_multiple_inheritance_python(): - from pybind11_tests import Base1, Base2 - class MI1(Base1, Base2): + class MI1(m.Base1, m.Base2): def __init__(self, i, j): - Base1.__init__(self, i) - Base2.__init__(self, j) + m.Base1.__init__(self, i) + m.Base2.__init__(self, j) class B1(object): def v(self): return 1 - class MI2(B1, Base1, Base2): + class MI2(B1, m.Base1, m.Base2): def __init__(self, i, j): B1.__init__(self) - Base1.__init__(self, i) - Base2.__init__(self, j) + m.Base1.__init__(self, i) + m.Base2.__init__(self, j) class MI3(MI2): def __init__(self, i, j): MI2.__init__(self, i, j) - class MI4(MI3, Base2): + class MI4(MI3, m.Base2): def __init__(self, i, j, k): MI2.__init__(self, j, k) - Base2.__init__(self, i) + m.Base2.__init__(self, i) - class MI5(Base2, B1, Base1): + class MI5(m.Base2, B1, m.Base1): def __init__(self, i, j): B1.__init__(self) - Base1.__init__(self, i) - Base2.__init__(self, j) + m.Base1.__init__(self, i) + m.Base2.__init__(self, j) - class MI6(Base2, B1): + class MI6(m.Base2, B1): def __init__(self, i): - Base2.__init__(self, i) + m.Base2.__init__(self, i) B1.__init__(self) class B2(B1): @@ -160,47 +155,45 @@ def test_multiple_inheritance_python(): def test_multiple_inheritance_python_many_bases(): - from pybind11_tests import (BaseN1, BaseN2, BaseN3, BaseN4, BaseN5, BaseN6, BaseN7, - BaseN8, BaseN9, BaseN10, BaseN11, BaseN12, BaseN13, BaseN14, - BaseN15, BaseN16, BaseN17) - class MIMany14(BaseN1, BaseN2, BaseN3, BaseN4): + class MIMany14(m.BaseN1, m.BaseN2, m.BaseN3, m.BaseN4): def __init__(self): - BaseN1.__init__(self, 1) - BaseN2.__init__(self, 2) - BaseN3.__init__(self, 3) - BaseN4.__init__(self, 4) + m.BaseN1.__init__(self, 1) + m.BaseN2.__init__(self, 2) + m.BaseN3.__init__(self, 3) + m.BaseN4.__init__(self, 4) - class MIMany58(BaseN5, BaseN6, BaseN7, BaseN8): + class MIMany58(m.BaseN5, m.BaseN6, m.BaseN7, m.BaseN8): def __init__(self): - BaseN5.__init__(self, 5) - BaseN6.__init__(self, 6) - BaseN7.__init__(self, 7) - BaseN8.__init__(self, 8) + m.BaseN5.__init__(self, 5) + m.BaseN6.__init__(self, 6) + m.BaseN7.__init__(self, 7) + m.BaseN8.__init__(self, 8) - class MIMany916(BaseN9, BaseN10, BaseN11, BaseN12, BaseN13, BaseN14, BaseN15, BaseN16): + class MIMany916(m.BaseN9, m.BaseN10, m.BaseN11, m.BaseN12, m.BaseN13, m.BaseN14, m.BaseN15, + m.BaseN16): def __init__(self): - BaseN9.__init__(self, 9) - BaseN10.__init__(self, 10) - BaseN11.__init__(self, 11) - BaseN12.__init__(self, 12) - BaseN13.__init__(self, 13) - BaseN14.__init__(self, 14) - BaseN15.__init__(self, 15) - BaseN16.__init__(self, 16) + m.BaseN9.__init__(self, 9) + m.BaseN10.__init__(self, 10) + m.BaseN11.__init__(self, 11) + m.BaseN12.__init__(self, 12) + m.BaseN13.__init__(self, 13) + m.BaseN14.__init__(self, 14) + m.BaseN15.__init__(self, 15) + m.BaseN16.__init__(self, 16) - class MIMany19(MIMany14, MIMany58, BaseN9): + class MIMany19(MIMany14, MIMany58, m.BaseN9): def __init__(self): MIMany14.__init__(self) MIMany58.__init__(self) - BaseN9.__init__(self, 9) + m.BaseN9.__init__(self, 9) - class MIMany117(MIMany14, MIMany58, MIMany916, BaseN17): + class MIMany117(MIMany14, MIMany58, MIMany916, m.BaseN17): def __init__(self): MIMany14.__init__(self) MIMany58.__init__(self) MIMany916.__init__(self) - BaseN17.__init__(self, 17) + m.BaseN17.__init__(self, 17) # Inherits from 4 registered C++ classes: can fit in one pointer on any modern arch: a = MIMany14() @@ -224,31 +217,29 @@ def test_multiple_inheritance_python_many_bases(): def test_multiple_inheritance_virtbase(): - from pybind11_tests import Base12a, bar_base2a, bar_base2a_sharedptr - class MITypePy(Base12a): + class MITypePy(m.Base12a): def __init__(self, i, j): - Base12a.__init__(self, i, j) + m.Base12a.__init__(self, i, j) mt = MITypePy(3, 4) assert mt.bar() == 4 - assert bar_base2a(mt) == 4 - assert bar_base2a_sharedptr(mt) == 4 + assert m.bar_base2a(mt) == 4 + assert m.bar_base2a_sharedptr(mt) == 4 def test_mi_static_properties(): """Mixing bases with and without static properties should be possible and the result should be independent of base definition order""" - from pybind11_tests import mi - for d in (mi.VanillaStaticMix1(), mi.VanillaStaticMix2()): + for d in (m.VanillaStaticMix1(), m.VanillaStaticMix2()): assert d.vanilla() == "Vanilla" assert d.static_func1() == "WithStatic1" assert d.static_func2() == "WithStatic2" assert d.static_func() == d.__class__.__name__ - mi.WithStatic1.static_value1 = 1 - mi.WithStatic2.static_value2 = 2 + m.WithStatic1.static_value1 = 1 + m.WithStatic2.static_value2 = 2 assert d.static_value1 == 1 assert d.static_value2 == 2 assert d.static_value == 12 @@ -264,30 +255,28 @@ def test_mi_static_properties(): @pytest.unsupported_on_pypy def test_mi_dynamic_attributes(): """Mixing bases with and without dynamic attribute support""" - from pybind11_tests import mi - for d in (mi.VanillaDictMix1(), mi.VanillaDictMix2()): + for d in (m.VanillaDictMix1(), m.VanillaDictMix2()): d.dynamic = 1 assert d.dynamic == 1 def test_mi_unaligned_base(): """Returning an offset (non-first MI) base class pointer should recognize the instance""" - from pybind11_tests import I801C, I801D, i801b1_c, i801b2_c, i801b1_d, i801b2_d n_inst = ConstructorStats.detail_reg_inst() - c = I801C() - d = I801D() + c = m.I801C() + d = m.I801D() # + 4 below because we have the two instances, and each instance has offset base I801B2 assert ConstructorStats.detail_reg_inst() == n_inst + 4 - b1c = i801b1_c(c) + b1c = m.i801b1_c(c) assert b1c is c - b2c = i801b2_c(c) + b2c = m.i801b2_c(c) assert b2c is c - b1d = i801b1_d(d) + b1d = m.i801b1_d(d) assert b1d is d - b2d = i801b2_d(d) + b2d = m.i801b2_d(d) assert b2d is d assert ConstructorStats.detail_reg_inst() == n_inst + 4 # no extra instances @@ -299,30 +288,28 @@ def test_mi_unaligned_base(): def test_mi_base_return(): """Tests returning an offset (non-first MI) base class pointer to a derived instance""" - from pybind11_tests import (I801B2, I801C, I801D, i801c_b1, i801c_b2, i801d_b1, i801d_b2, - i801e_c, i801e_b2) n_inst = ConstructorStats.detail_reg_inst() - c1 = i801c_b1() - assert type(c1) is I801C + c1 = m.i801c_b1() + assert type(c1) is m.I801C assert c1.a == 1 assert c1.b == 2 - d1 = i801d_b1() - assert type(d1) is I801D + d1 = m.i801d_b1() + assert type(d1) is m.I801D assert d1.a == 1 assert d1.b == 2 assert ConstructorStats.detail_reg_inst() == n_inst + 4 - c2 = i801c_b2() - assert type(c2) is I801C + c2 = m.i801c_b2() + assert type(c2) is m.I801C assert c2.a == 1 assert c2.b == 2 - d2 = i801d_b2() - assert type(d2) is I801D + d2 = m.i801d_b2() + assert type(d2) is m.I801D assert d2.a == 1 assert d2.b == 2 @@ -336,11 +323,11 @@ def test_mi_base_return(): # Returning an unregistered derived type with a registered base; we won't # pick up the derived type, obviously, but should still work (as an object # of whatever type was returned). - e1 = i801e_c() - assert type(e1) is I801C + e1 = m.i801e_c() + assert type(e1) is m.I801C assert e1.a == 1 assert e1.b == 2 - e2 = i801e_b2() - assert type(e2) is I801B2 + e2 = m.i801e_b2() + assert type(e2) is m.I801B2 assert e2.b == 2