The source code and dockerfile for the GSW2024 AI Lab.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

239 lines
9.8 KiB

4 weeks ago
  1. /*
  2. tests/test_class.cpp -- test py::class_ definitions and basic functionality
  3. Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
  4. All rights reserved. Use of this source code is governed by a
  5. BSD-style license that can be found in the LICENSE file.
  6. */
  7. #include "pybind11_tests.h"
  8. #include "constructor_stats.h"
  9. TEST_SUBMODULE(class_, m) {
  10. // test_instance
  11. struct NoConstructor {
  12. static NoConstructor *new_instance() {
  13. auto *ptr = new NoConstructor();
  14. print_created(ptr, "via new_instance");
  15. return ptr;
  16. }
  17. ~NoConstructor() { print_destroyed(this); }
  18. };
  19. py::class_<NoConstructor>(m, "NoConstructor")
  20. .def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
  21. // test_inheritance
  22. class Pet {
  23. public:
  24. Pet(const std::string &name, const std::string &species)
  25. : m_name(name), m_species(species) {}
  26. std::string name() const { return m_name; }
  27. std::string species() const { return m_species; }
  28. private:
  29. std::string m_name;
  30. std::string m_species;
  31. };
  32. class Dog : public Pet {
  33. public:
  34. Dog(const std::string &name) : Pet(name, "dog") {}
  35. std::string bark() const { return "Woof!"; }
  36. };
  37. class Rabbit : public Pet {
  38. public:
  39. Rabbit(const std::string &name) : Pet(name, "parrot") {}
  40. };
  41. class Hamster : public Pet {
  42. public:
  43. Hamster(const std::string &name) : Pet(name, "rodent") {}
  44. };
  45. class Chimera : public Pet {
  46. Chimera() : Pet("Kimmy", "chimera") {}
  47. };
  48. py::class_<Pet> pet_class(m, "Pet");
  49. pet_class
  50. .def(py::init<std::string, std::string>())
  51. .def("name", &Pet::name)
  52. .def("species", &Pet::species);
  53. /* One way of declaring a subclass relationship: reference parent's class_ object */
  54. py::class_<Dog>(m, "Dog", pet_class)
  55. .def(py::init<std::string>());
  56. /* Another way of declaring a subclass relationship: reference parent's C++ type */
  57. py::class_<Rabbit, Pet>(m, "Rabbit")
  58. .def(py::init<std::string>());
  59. /* And another: list parent in class template arguments */
  60. py::class_<Hamster, Pet>(m, "Hamster")
  61. .def(py::init<std::string>());
  62. /* Constructors are not inherited by default */
  63. py::class_<Chimera, Pet>(m, "Chimera");
  64. m.def("pet_name_species", [](const Pet &pet) { return pet.name() + " is a " + pet.species(); });
  65. m.def("dog_bark", [](const Dog &dog) { return dog.bark(); });
  66. // test_automatic_upcasting
  67. struct BaseClass { virtual ~BaseClass() {} };
  68. struct DerivedClass1 : BaseClass { };
  69. struct DerivedClass2 : BaseClass { };
  70. py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
  71. py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
  72. py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
  73. m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); });
  74. m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); });
  75. m.def("return_class_n", [](int n) -> BaseClass* {
  76. if (n == 1) return new DerivedClass1();
  77. if (n == 2) return new DerivedClass2();
  78. return new BaseClass();
  79. });
  80. m.def("return_none", []() -> BaseClass* { return nullptr; });
  81. // test_isinstance
  82. m.def("check_instances", [](py::list l) {
  83. return py::make_tuple(
  84. py::isinstance<py::tuple>(l[0]),
  85. py::isinstance<py::dict>(l[1]),
  86. py::isinstance<Pet>(l[2]),
  87. py::isinstance<Pet>(l[3]),
  88. py::isinstance<Dog>(l[4]),
  89. py::isinstance<Rabbit>(l[5]),
  90. py::isinstance<UnregisteredType>(l[6])
  91. );
  92. });
  93. // test_mismatched_holder
  94. struct MismatchBase1 { };
  95. struct MismatchDerived1 : MismatchBase1 { };
  96. struct MismatchBase2 { };
  97. struct MismatchDerived2 : MismatchBase2 { };
  98. m.def("mismatched_holder_1", []() {
  99. auto mod = py::module::import("__main__");
  100. py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(mod, "MismatchBase1");
  101. py::class_<MismatchDerived1, MismatchBase1>(mod, "MismatchDerived1");
  102. });
  103. m.def("mismatched_holder_2", []() {
  104. auto mod = py::module::import("__main__");
  105. py::class_<MismatchBase2>(mod, "MismatchBase2");
  106. py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>,
  107. MismatchBase2>(mod, "MismatchDerived2");
  108. });
  109. // test_override_static
  110. // #511: problem with inheritance + overwritten def_static
  111. struct MyBase {
  112. static std::unique_ptr<MyBase> make() {
  113. return std::unique_ptr<MyBase>(new MyBase());
  114. }
  115. };
  116. struct MyDerived : MyBase {
  117. static std::unique_ptr<MyDerived> make() {
  118. return std::unique_ptr<MyDerived>(new MyDerived());
  119. }
  120. };
  121. py::class_<MyBase>(m, "MyBase")
  122. .def_static("make", &MyBase::make);
  123. py::class_<MyDerived, MyBase>(m, "MyDerived")
  124. .def_static("make", &MyDerived::make)
  125. .def_static("make2", &MyDerived::make);
  126. // test_implicit_conversion_life_support
  127. struct ConvertibleFromUserType {
  128. int i;
  129. ConvertibleFromUserType(UserType u) : i(u.value()) { }
  130. };
  131. py::class_<ConvertibleFromUserType>(m, "AcceptsUserType")
  132. .def(py::init<UserType>());
  133. py::implicitly_convertible<UserType, ConvertibleFromUserType>();
  134. m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; });
  135. m.def("implicitly_convert_variable", [](py::object o) {
  136. // `o` is `UserType` and `r` is a reference to a temporary created by implicit
  137. // conversion. This is valid when called inside a bound function because the temp
  138. // object is attached to the same life support system as the arguments.
  139. const auto &r = o.cast<const ConvertibleFromUserType &>();
  140. return r.i;
  141. });
  142. m.add_object("implicitly_convert_variable_fail", [&] {
  143. auto f = [](PyObject *, PyObject *args) -> PyObject * {
  144. auto o = py::reinterpret_borrow<py::tuple>(args)[0];
  145. try { // It should fail here because there is no life support.
  146. o.cast<const ConvertibleFromUserType &>();
  147. } catch (const py::cast_error &e) {
  148. return py::str(e.what()).release().ptr();
  149. }
  150. return py::str().release().ptr();
  151. };
  152. auto def = new PyMethodDef{"f", f, METH_VARARGS, nullptr};
  153. return py::reinterpret_steal<py::object>(PyCFunction_NewEx(def, nullptr, m.ptr()));
  154. }());
  155. }
  156. template <int N> class BreaksBase {};
  157. template <int N> class BreaksTramp : public BreaksBase<N> {};
  158. // These should all compile just fine:
  159. typedef py::class_<BreaksBase<1>, std::unique_ptr<BreaksBase<1>>, BreaksTramp<1>> DoesntBreak1;
  160. typedef py::class_<BreaksBase<2>, BreaksTramp<2>, std::unique_ptr<BreaksBase<2>>> DoesntBreak2;
  161. typedef py::class_<BreaksBase<3>, std::unique_ptr<BreaksBase<3>>> DoesntBreak3;
  162. typedef py::class_<BreaksBase<4>, BreaksTramp<4>> DoesntBreak4;
  163. typedef py::class_<BreaksBase<5>> DoesntBreak5;
  164. typedef py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>> DoesntBreak6;
  165. typedef py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>> DoesntBreak7;
  166. typedef py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>> DoesntBreak8;
  167. #define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<N>>::value, \
  168. "DoesntBreak" #N " has wrong type!")
  169. CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8);
  170. #define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<N>>::value, \
  171. "DoesntBreak" #N " has wrong type_alias!")
  172. #define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void<typename DoesntBreak##N::type_alias>::value, \
  173. "DoesntBreak" #N " has type alias, but shouldn't!")
  174. CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8);
  175. #define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<N>>>::value, \
  176. "DoesntBreak" #N " has wrong holder_type!")
  177. CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique);
  178. CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
  179. // There's no nice way to test that these fail because they fail to compile; leave them here,
  180. // though, so that they can be manually tested by uncommenting them (and seeing that compilation
  181. // failures occurs).
  182. // We have to actually look into the type: the typedef alone isn't enough to instantiate the type:
  183. #define CHECK_BROKEN(N) static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-N>>::value, \
  184. "Breaks1 has wrong type!");
  185. //// Two holder classes:
  186. //typedef py::class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>> Breaks1;
  187. //CHECK_BROKEN(1);
  188. //// Two aliases:
  189. //typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2;
  190. //CHECK_BROKEN(2);
  191. //// Holder + 2 aliases
  192. //typedef py::class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>> Breaks3;
  193. //CHECK_BROKEN(3);
  194. //// Alias + 2 holders
  195. //typedef py::class_<BreaksBase<-4>, std::unique_ptr<BreaksBase<-4>>, BreaksTramp<-4>, std::shared_ptr<BreaksBase<-4>>> Breaks4;
  196. //CHECK_BROKEN(4);
  197. //// Invalid option (not a subclass or holder)
  198. //typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5;
  199. //CHECK_BROKEN(5);
  200. //// Invalid option: multiple inheritance not supported:
  201. //template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
  202. //typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8;
  203. //CHECK_BROKEN(8);