Jason Lowe-Power | f07d506 | 2017-11-17 17:02:05 -0800 | [diff] [blame] | 1 | /* |
| 2 | tests/test_factory_constructors.cpp -- tests construction from a factory function |
| 3 | via py::init_factory() |
| 4 | |
| 5 | Copyright (c) 2017 Jason Rhinelander <jason@imaginary.ca> |
| 6 | |
| 7 | All rights reserved. Use of this source code is governed by a |
| 8 | BSD-style license that can be found in the LICENSE file. |
| 9 | */ |
| 10 | |
| 11 | #include "pybind11_tests.h" |
| 12 | #include "constructor_stats.h" |
| 13 | #include <cmath> |
| 14 | |
| 15 | // Classes for testing python construction via C++ factory function: |
| 16 | // Not publically constructible, copyable, or movable: |
| 17 | class TestFactory1 { |
| 18 | friend class TestFactoryHelper; |
| 19 | TestFactory1() : value("(empty)") { print_default_created(this); } |
| 20 | TestFactory1(int v) : value(std::to_string(v)) { print_created(this, value); } |
| 21 | TestFactory1(std::string v) : value(std::move(v)) { print_created(this, value); } |
| 22 | TestFactory1(TestFactory1 &&) = delete; |
| 23 | TestFactory1(const TestFactory1 &) = delete; |
| 24 | TestFactory1 &operator=(TestFactory1 &&) = delete; |
| 25 | TestFactory1 &operator=(const TestFactory1 &) = delete; |
| 26 | public: |
| 27 | std::string value; |
| 28 | ~TestFactory1() { print_destroyed(this); } |
| 29 | }; |
| 30 | // Non-public construction, but moveable: |
| 31 | class TestFactory2 { |
| 32 | friend class TestFactoryHelper; |
| 33 | TestFactory2() : value("(empty2)") { print_default_created(this); } |
| 34 | TestFactory2(int v) : value(std::to_string(v)) { print_created(this, value); } |
| 35 | TestFactory2(std::string v) : value(std::move(v)) { print_created(this, value); } |
| 36 | public: |
| 37 | TestFactory2(TestFactory2 &&m) { value = std::move(m.value); print_move_created(this); } |
| 38 | TestFactory2 &operator=(TestFactory2 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; } |
| 39 | std::string value; |
| 40 | ~TestFactory2() { print_destroyed(this); } |
| 41 | }; |
| 42 | // Mixed direct/factory construction: |
| 43 | class TestFactory3 { |
| 44 | protected: |
| 45 | friend class TestFactoryHelper; |
| 46 | TestFactory3() : value("(empty3)") { print_default_created(this); } |
| 47 | TestFactory3(int v) : value(std::to_string(v)) { print_created(this, value); } |
| 48 | public: |
| 49 | TestFactory3(std::string v) : value(std::move(v)) { print_created(this, value); } |
| 50 | TestFactory3(TestFactory3 &&m) { value = std::move(m.value); print_move_created(this); } |
| 51 | TestFactory3 &operator=(TestFactory3 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; } |
| 52 | std::string value; |
| 53 | virtual ~TestFactory3() { print_destroyed(this); } |
| 54 | }; |
| 55 | // Inheritance test |
| 56 | class TestFactory4 : public TestFactory3 { |
| 57 | public: |
| 58 | TestFactory4() : TestFactory3() { print_default_created(this); } |
| 59 | TestFactory4(int v) : TestFactory3(v) { print_created(this, v); } |
| 60 | virtual ~TestFactory4() { print_destroyed(this); } |
| 61 | }; |
| 62 | // Another class for an invalid downcast test |
| 63 | class TestFactory5 : public TestFactory3 { |
| 64 | public: |
| 65 | TestFactory5(int i) : TestFactory3(i) { print_created(this, i); } |
| 66 | virtual ~TestFactory5() { print_destroyed(this); } |
| 67 | }; |
| 68 | |
| 69 | class TestFactory6 { |
| 70 | protected: |
| 71 | int value; |
| 72 | bool alias = false; |
| 73 | public: |
| 74 | TestFactory6(int i) : value{i} { print_created(this, i); } |
| 75 | TestFactory6(TestFactory6 &&f) { print_move_created(this); value = f.value; alias = f.alias; } |
| 76 | TestFactory6(const TestFactory6 &f) { print_copy_created(this); value = f.value; alias = f.alias; } |
| 77 | virtual ~TestFactory6() { print_destroyed(this); } |
| 78 | virtual int get() { return value; } |
| 79 | bool has_alias() { return alias; } |
| 80 | }; |
| 81 | class PyTF6 : public TestFactory6 { |
| 82 | public: |
| 83 | // Special constructor that allows the factory to construct a PyTF6 from a TestFactory6 only |
| 84 | // when an alias is needed: |
| 85 | PyTF6(TestFactory6 &&base) : TestFactory6(std::move(base)) { alias = true; print_created(this, "move", value); } |
| 86 | PyTF6(int i) : TestFactory6(i) { alias = true; print_created(this, i); } |
| 87 | PyTF6(PyTF6 &&f) : TestFactory6(std::move(f)) { print_move_created(this); } |
| 88 | PyTF6(const PyTF6 &f) : TestFactory6(f) { print_copy_created(this); } |
| 89 | PyTF6(std::string s) : TestFactory6((int) s.size()) { alias = true; print_created(this, s); } |
| 90 | virtual ~PyTF6() { print_destroyed(this); } |
| 91 | int get() override { PYBIND11_OVERLOAD(int, TestFactory6, get, /*no args*/); } |
| 92 | }; |
| 93 | |
| 94 | class TestFactory7 { |
| 95 | protected: |
| 96 | int value; |
| 97 | bool alias = false; |
| 98 | public: |
| 99 | TestFactory7(int i) : value{i} { print_created(this, i); } |
| 100 | TestFactory7(TestFactory7 &&f) { print_move_created(this); value = f.value; alias = f.alias; } |
| 101 | TestFactory7(const TestFactory7 &f) { print_copy_created(this); value = f.value; alias = f.alias; } |
| 102 | virtual ~TestFactory7() { print_destroyed(this); } |
| 103 | virtual int get() { return value; } |
| 104 | bool has_alias() { return alias; } |
| 105 | }; |
| 106 | class PyTF7 : public TestFactory7 { |
| 107 | public: |
| 108 | PyTF7(int i) : TestFactory7(i) { alias = true; print_created(this, i); } |
| 109 | PyTF7(PyTF7 &&f) : TestFactory7(std::move(f)) { print_move_created(this); } |
| 110 | PyTF7(const PyTF7 &f) : TestFactory7(f) { print_copy_created(this); } |
| 111 | virtual ~PyTF7() { print_destroyed(this); } |
| 112 | int get() override { PYBIND11_OVERLOAD(int, TestFactory7, get, /*no args*/); } |
| 113 | }; |
| 114 | |
| 115 | |
| 116 | class TestFactoryHelper { |
| 117 | public: |
| 118 | // Non-movable, non-copyable type: |
| 119 | // Return via pointer: |
| 120 | static TestFactory1 *construct1() { return new TestFactory1(); } |
| 121 | // Holder: |
| 122 | static std::unique_ptr<TestFactory1> construct1(int a) { return std::unique_ptr<TestFactory1>(new TestFactory1(a)); } |
| 123 | // pointer again |
| 124 | static TestFactory1 *construct1_string(std::string a) { return new TestFactory1(a); } |
| 125 | |
| 126 | // Moveable type: |
| 127 | // pointer: |
| 128 | static TestFactory2 *construct2() { return new TestFactory2(); } |
| 129 | // holder: |
| 130 | static std::unique_ptr<TestFactory2> construct2(int a) { return std::unique_ptr<TestFactory2>(new TestFactory2(a)); } |
| 131 | // by value moving: |
| 132 | static TestFactory2 construct2(std::string a) { return TestFactory2(a); } |
| 133 | |
| 134 | // shared_ptr holder type: |
| 135 | // pointer: |
| 136 | static TestFactory3 *construct3() { return new TestFactory3(); } |
| 137 | // holder: |
| 138 | static std::shared_ptr<TestFactory3> construct3(int a) { return std::shared_ptr<TestFactory3>(new TestFactory3(a)); } |
| 139 | }; |
| 140 | |
| 141 | TEST_SUBMODULE(factory_constructors, m) { |
| 142 | |
| 143 | // Define various trivial types to allow simpler overload resolution: |
| 144 | py::module m_tag = m.def_submodule("tag"); |
| 145 | #define MAKE_TAG_TYPE(Name) \ |
| 146 | struct Name##_tag {}; \ |
| 147 | py::class_<Name##_tag>(m_tag, #Name "_tag").def(py::init<>()); \ |
| 148 | m_tag.attr(#Name) = py::cast(Name##_tag{}) |
| 149 | MAKE_TAG_TYPE(pointer); |
| 150 | MAKE_TAG_TYPE(unique_ptr); |
| 151 | MAKE_TAG_TYPE(move); |
| 152 | MAKE_TAG_TYPE(shared_ptr); |
| 153 | MAKE_TAG_TYPE(derived); |
| 154 | MAKE_TAG_TYPE(TF4); |
| 155 | MAKE_TAG_TYPE(TF5); |
| 156 | MAKE_TAG_TYPE(null_ptr); |
| 157 | MAKE_TAG_TYPE(base); |
| 158 | MAKE_TAG_TYPE(invalid_base); |
| 159 | MAKE_TAG_TYPE(alias); |
| 160 | MAKE_TAG_TYPE(unaliasable); |
| 161 | MAKE_TAG_TYPE(mixed); |
| 162 | |
| 163 | // test_init_factory_basic, test_bad_type |
| 164 | py::class_<TestFactory1>(m, "TestFactory1") |
| 165 | .def(py::init([](unique_ptr_tag, int v) { return TestFactoryHelper::construct1(v); })) |
| 166 | .def(py::init(&TestFactoryHelper::construct1_string)) // raw function pointer |
| 167 | .def(py::init([](pointer_tag) { return TestFactoryHelper::construct1(); })) |
| 168 | .def(py::init([](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); })) |
| 169 | .def_readwrite("value", &TestFactory1::value) |
| 170 | ; |
| 171 | py::class_<TestFactory2>(m, "TestFactory2") |
| 172 | .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); })) |
| 173 | .def(py::init([](unique_ptr_tag, std::string v) { return TestFactoryHelper::construct2(v); })) |
| 174 | .def(py::init([](move_tag) { return TestFactoryHelper::construct2(); })) |
| 175 | .def_readwrite("value", &TestFactory2::value) |
| 176 | ; |
| 177 | |
| 178 | // Stateful & reused: |
| 179 | int c = 1; |
| 180 | auto c4a = [c](pointer_tag, TF4_tag, int a) { (void) c; return new TestFactory4(a);}; |
| 181 | |
| 182 | // test_init_factory_basic, test_init_factory_casting |
| 183 | py::class_<TestFactory3, std::shared_ptr<TestFactory3>>(m, "TestFactory3") |
| 184 | .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); })) |
| 185 | .def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); })) |
| 186 | .def("__init__", [](TestFactory3 &self, std::string v) { new (&self) TestFactory3(v); }) // placement-new ctor |
| 187 | |
| 188 | // factories returning a derived type: |
| 189 | .def(py::init(c4a)) // derived ptr |
| 190 | .def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); })) |
| 191 | // derived shared ptr: |
| 192 | .def(py::init([](shared_ptr_tag, TF4_tag, int a) { return std::make_shared<TestFactory4>(a); })) |
| 193 | .def(py::init([](shared_ptr_tag, TF5_tag, int a) { return std::make_shared<TestFactory5>(a); })) |
| 194 | |
| 195 | // Returns nullptr: |
| 196 | .def(py::init([](null_ptr_tag) { return (TestFactory3 *) nullptr; })) |
| 197 | |
| 198 | .def_readwrite("value", &TestFactory3::value) |
| 199 | ; |
| 200 | |
| 201 | // test_init_factory_casting |
| 202 | py::class_<TestFactory4, TestFactory3, std::shared_ptr<TestFactory4>>(m, "TestFactory4") |
| 203 | .def(py::init(c4a)) // pointer |
| 204 | ; |
| 205 | |
| 206 | // Doesn't need to be registered, but registering makes getting ConstructorStats easier: |
| 207 | py::class_<TestFactory5, TestFactory3, std::shared_ptr<TestFactory5>>(m, "TestFactory5"); |
| 208 | |
| 209 | // test_init_factory_alias |
| 210 | // Alias testing |
| 211 | py::class_<TestFactory6, PyTF6>(m, "TestFactory6") |
| 212 | .def(py::init([](base_tag, int i) { return TestFactory6(i); })) |
| 213 | .def(py::init([](alias_tag, int i) { return PyTF6(i); })) |
| 214 | .def(py::init([](alias_tag, std::string s) { return PyTF6(s); })) |
| 215 | .def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF6(i); })) |
| 216 | .def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory6(i); })) |
| 217 | .def(py::init([](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); })) |
| 218 | |
| 219 | .def("get", &TestFactory6::get) |
| 220 | .def("has_alias", &TestFactory6::has_alias) |
| 221 | |
| 222 | .def_static("get_cstats", &ConstructorStats::get<TestFactory6>, py::return_value_policy::reference) |
| 223 | .def_static("get_alias_cstats", &ConstructorStats::get<PyTF6>, py::return_value_policy::reference) |
| 224 | ; |
| 225 | |
| 226 | // test_init_factory_dual |
| 227 | // Separate alias constructor testing |
| 228 | py::class_<TestFactory7, PyTF7, std::shared_ptr<TestFactory7>>(m, "TestFactory7") |
| 229 | .def(py::init( |
| 230 | [](int i) { return TestFactory7(i); }, |
| 231 | [](int i) { return PyTF7(i); })) |
| 232 | .def(py::init( |
| 233 | [](pointer_tag, int i) { return new TestFactory7(i); }, |
| 234 | [](pointer_tag, int i) { return new PyTF7(i); })) |
| 235 | .def(py::init( |
| 236 | [](mixed_tag, int i) { return new TestFactory7(i); }, |
| 237 | [](mixed_tag, int i) { return PyTF7(i); })) |
| 238 | .def(py::init( |
| 239 | [](mixed_tag, std::string s) { return TestFactory7((int) s.size()); }, |
| 240 | [](mixed_tag, std::string s) { return new PyTF7((int) s.size()); })) |
| 241 | .def(py::init( |
| 242 | [](base_tag, pointer_tag, int i) { return new TestFactory7(i); }, |
| 243 | [](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); })) |
| 244 | .def(py::init( |
| 245 | [](alias_tag, pointer_tag, int i) { return new PyTF7(i); }, |
| 246 | [](alias_tag, pointer_tag, int i) { return new PyTF7(10*i); })) |
| 247 | .def(py::init( |
| 248 | [](shared_ptr_tag, base_tag, int i) { return std::make_shared<TestFactory7>(i); }, |
| 249 | [](shared_ptr_tag, base_tag, int i) { auto *p = new PyTF7(i); return std::shared_ptr<TestFactory7>(p); })) |
| 250 | .def(py::init( |
| 251 | [](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared<TestFactory7>(i); }, |
| 252 | [](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared<TestFactory7>(i); })) // <-- invalid alias factory |
| 253 | |
| 254 | .def("get", &TestFactory7::get) |
| 255 | .def("has_alias", &TestFactory7::has_alias) |
| 256 | |
| 257 | .def_static("get_cstats", &ConstructorStats::get<TestFactory7>, py::return_value_policy::reference) |
| 258 | .def_static("get_alias_cstats", &ConstructorStats::get<PyTF7>, py::return_value_policy::reference) |
| 259 | ; |
| 260 | |
| 261 | // test_placement_new_alternative |
| 262 | // Class with a custom new operator but *without* a placement new operator (issue #948) |
| 263 | class NoPlacementNew { |
| 264 | public: |
| 265 | NoPlacementNew(int i) : i(i) { } |
| 266 | static void *operator new(std::size_t s) { |
| 267 | auto *p = ::operator new(s); |
| 268 | py::print("operator new called, returning", reinterpret_cast<uintptr_t>(p)); |
| 269 | return p; |
| 270 | } |
| 271 | static void operator delete(void *p) { |
| 272 | py::print("operator delete called on", reinterpret_cast<uintptr_t>(p)); |
| 273 | ::operator delete(p); |
| 274 | } |
| 275 | int i; |
| 276 | }; |
| 277 | // As of 2.2, `py::init<args>` no longer requires placement new |
| 278 | py::class_<NoPlacementNew>(m, "NoPlacementNew") |
| 279 | .def(py::init<int>()) |
| 280 | .def(py::init([]() { return new NoPlacementNew(100); })) |
| 281 | .def_readwrite("i", &NoPlacementNew::i) |
| 282 | ; |
| 283 | |
| 284 | |
| 285 | // test_reallocations |
| 286 | // Class that has verbose operator_new/operator_delete calls |
| 287 | struct NoisyAlloc { |
| 288 | NoisyAlloc(int i) { py::print(py::str("NoisyAlloc(int {})").format(i)); } |
| 289 | NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); } |
| 290 | ~NoisyAlloc() { py::print("~NoisyAlloc()"); } |
| 291 | |
| 292 | static void *operator new(size_t s) { py::print("noisy new"); return ::operator new(s); } |
| 293 | static void *operator new(size_t, void *p) { py::print("noisy placement new"); return p; } |
| 294 | static void operator delete(void *p, size_t) { py::print("noisy delete"); ::operator delete(p); } |
| 295 | static void operator delete(void *, void *) { py::print("noisy placement delete"); } |
| 296 | #if defined(_MSC_VER) && _MSC_VER < 1910 |
| 297 | // MSVC 2015 bug: the above "noisy delete" isn't invoked (fixed in MSVC 2017) |
| 298 | static void operator delete(void *p) { py::print("noisy delete"); ::operator delete(p); } |
| 299 | #endif |
| 300 | }; |
| 301 | py::class_<NoisyAlloc>(m, "NoisyAlloc") |
| 302 | // Since these overloads have the same number of arguments, the dispatcher will try each of |
| 303 | // them until the arguments convert. Thus we can get a pre-allocation here when passing a |
| 304 | // single non-integer: |
| 305 | .def("__init__", [](NoisyAlloc *a, int i) { new (a) NoisyAlloc(i); }) // Regular constructor, runs first, requires preallocation |
| 306 | .def(py::init([](double d) { return new NoisyAlloc(d); })) |
| 307 | |
| 308 | // The two-argument version: first the factory pointer overload. |
| 309 | .def(py::init([](int i, int) { return new NoisyAlloc(i); })) |
| 310 | // Return-by-value: |
| 311 | .def(py::init([](double d, int) { return NoisyAlloc(d); })) |
| 312 | // Old-style placement new init; requires preallocation |
| 313 | .def("__init__", [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); }) |
| 314 | // Requires deallocation of previous overload preallocated value: |
| 315 | .def(py::init([](int i, double) { return new NoisyAlloc(i); })) |
| 316 | // Regular again: requires yet another preallocation |
| 317 | .def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); }) |
| 318 | ; |
| 319 | |
| 320 | |
| 321 | |
| 322 | |
| 323 | // static_assert testing (the following def's should all fail with appropriate compilation errors): |
| 324 | #if 0 |
| 325 | struct BadF1Base {}; |
| 326 | struct BadF1 : BadF1Base {}; |
| 327 | struct PyBadF1 : BadF1 {}; |
| 328 | py::class_<BadF1, PyBadF1, std::shared_ptr<BadF1>> bf1(m, "BadF1"); |
| 329 | // wrapped factory function must return a compatible pointer, holder, or value |
| 330 | bf1.def(py::init([]() { return 3; })); |
| 331 | // incompatible factory function pointer return type |
| 332 | bf1.def(py::init([]() { static int three = 3; return &three; })); |
| 333 | // incompatible factory function std::shared_ptr<T> return type: cannot convert shared_ptr<T> to holder |
| 334 | // (non-polymorphic base) |
| 335 | bf1.def(py::init([]() { return std::shared_ptr<BadF1Base>(new BadF1()); })); |
| 336 | #endif |
| 337 | } |