Scarab  v2.11.1
Project 8 C++ Utility Library
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 "pybind11_tests.h"
12 #include "constructor_stats.h"
13 #include <cmath>
14 
15 // Classes for testing python construction via C++ factory function:
16 // Not publicly 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;
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:
38  TestFactory2 &operator=(TestFactory2 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; }
39  std::string value;
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); }
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:
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); }
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); }
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 
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 
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);
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); }))
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(); }))
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
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 
199  ;
200 
201  // test_init_factory_casting
203  .def(py::init(c4a)) // pointer
204  ;
205 
206  // Doesn't need to be registered, but registering makes getting ConstructorStats easier:
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
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); }))
282  ;
283 
284 
285  // test_reallocations
286  // Class that has verbose operator_new/operator_delete calls
287  struct NoisyAlloc {
288  NoisyAlloc(const NoisyAlloc &) = default;
289  NoisyAlloc(int i) { py::print(py::str("NoisyAlloc(int {})").format(i)); }
290  NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); }
291  ~NoisyAlloc() { py::print("~NoisyAlloc()"); }
292 
293  static void *operator new(size_t s) { py::print("noisy new"); return ::operator new(s); }
294  static void *operator new(size_t, void *p) { py::print("noisy placement new"); return p; }
295  static void operator delete(void *p, size_t) { py::print("noisy delete"); ::operator delete(p); }
296  static void operator delete(void *, void *) { py::print("noisy placement delete"); }
297 #if defined(_MSC_VER) && _MSC_VER < 1910
298  // MSVC 2015 bug: the above "noisy delete" isn't invoked (fixed in MSVC 2017)
299  static void operator delete(void *p) { py::print("noisy delete"); ::operator delete(p); }
300 #endif
301  };
302  py::class_<NoisyAlloc>(m, "NoisyAlloc")
303  // Since these overloads have the same number of arguments, the dispatcher will try each of
304  // them until the arguments convert. Thus we can get a pre-allocation here when passing a
305  // single non-integer:
306  .def("__init__", [](NoisyAlloc *a, int i) { new (a) NoisyAlloc(i); }) // Regular constructor, runs first, requires preallocation
307  .def(py::init([](double d) { return new NoisyAlloc(d); }))
308 
309  // The two-argument version: first the factory pointer overload.
310  .def(py::init([](int i, int) { return new NoisyAlloc(i); }))
311  // Return-by-value:
312  .def(py::init([](double d, int) { return NoisyAlloc(d); }))
313  // Old-style placement new init; requires preallocation
314  .def("__init__", [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); })
315  // Requires deallocation of previous overload preallocated value:
316  .def(py::init([](int i, double) { return new NoisyAlloc(i); }))
317  // Regular again: requires yet another preallocation
318  .def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); })
319  ;
320 
321 
322 
323 
324  // static_assert testing (the following def's should all fail with appropriate compilation errors):
325 #if 0
326  struct BadF1Base {};
327  struct BadF1 : BadF1Base {};
328  struct PyBadF1 : BadF1 {};
330  // wrapped factory function must return a compatible pointer, holder, or value
331  bf1.def(py::init([]() { return 3; }));
332  // incompatible factory function pointer return type
333  bf1.def(py::init([]() { static int three = 3; return &three; }));
334  // incompatible factory function std::shared_ptr<T> return type: cannot convert shared_ptr<T> to holder
335  // (non-polymorphic base)
336  bf1.def(py::init([]() { return std::shared_ptr<BadF1Base>(new BadF1()); }));
337 #endif
338 }
static TestFactory1 * construct1_string(std::string a)
std::size_t size_t
TestFactory6(TestFactory6 &&f)
static TestFactory3 * construct3()
TestFactory2(TestFactory2 &&m)
void print_destroyed(T *inst, Values &&...values)
STL namespace.
PyTF6(TestFactory6 &&base)
TestFactory1(std::string v)
void print_copy_created(T *inst, Values &&...values)
auto format(const std::locale &loc, const CharT *fmt, const Streamable &tp) -> decltype(to_stream(std::declval< std::basic_ostream< CharT > &>(), fmt, tp), std::basic_string< CharT >
Definition: date.h:5663
test_initializer factory_constructors("factory_constructors", test_submodule_factory_constructors)
TestFactory3(std::string v)
Wrapper for Python extension modules.
Definition: pybind11.h:789
TestFactory3(TestFactory3 &&m)
void print_default_created(T *inst, Values &&...values)
static TestFactory2 * construct2()
detail::initimpl::constructor< Args... > init()
Binds an existing constructor taking arguments Args...
Definition: pybind11.h:1371
static TestFactory1 * construct1()
TestFactory3 & operator=(TestFactory3 &&m)
void print_move_assigned(T *inst, Values &&...values)
static std::shared_ptr< TestFactory3 > construct3(int a)
TestFactory2(std::string v)
TestFactory2 & operator=(TestFactory2 &&m)
TestFactory1 & operator=(TestFactory1 &&)=delete
TestFactory7(const TestFactory7 &f)
PyTF6(const PyTF6 &f)
def d(s)
Definition: mkdoc.py:69
class_ & def_static(const char *name_, Func &&f, const Extra &... extra)
Definition: pybind11.h:1118
static std::unique_ptr< TestFactory2 > construct2(int a)
static TestFactory2 construct2(std::string a)
PyTF7(const PyTF7 &f)
TestFactory6(const TestFactory6 &f)
void print_created(T *inst, Values &&...values)
detail::enable_if_t<!detail::move_never< T >::value, T > move(object &&obj)
Definition: cast.h:1685
#define MAKE_TAG_TYPE(Name)
PyTF6(std::string s)
static std::unique_ptr< TestFactory1 > construct1(int a)
void print(Args &&...args)
Definition: pybind11.h:1849
void print_move_created(T *inst, Values &&...values)
#define TEST_SUBMODULE(name, variable)
TestFactory7(TestFactory7 &&f)
bool typename Extra class_ & def(const char *name_, Func &&f, const Extra &... extra)
Definition: pybind11.h:1110
auto to_string(T &&value) -> decltype(std::forward< T >(value))
Convert an object to a string (directly forward if this can become a string)
Definition: CLI11.hpp:1028
#define PYBIND11_OVERLOAD(ret_type, cname, fn,...)
Definition: pybind11.h:2145
class_ & def_readwrite(const char *name, D C::*pm, const Extra &... extra)
Definition: pybind11.h:1186