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


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:37:45