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