Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:17:54

0001 /*
0002     tests/test_factory_constructors.cpp -- tests construction from a factory function
0003                                            via py::init_factory()
0004 
0005     Copyright (c) 2017 Jason Rhinelander <jason@imaginary.ca>
0006 
0007     All rights reserved. Use of this source code is governed by a
0008     BSD-style license that can be found in the LICENSE file.
0009 */
0010 
0011 #include "constructor_stats.h"
0012 #include "pybind11_tests.h"
0013 
0014 #include <cmath>
0015 #include <new>
0016 #include <utility>
0017 
0018 // Classes for testing python construction via C++ factory function:
0019 // Not publicly constructible, copyable, or movable:
0020 class TestFactory1 {
0021     friend class TestFactoryHelper;
0022     TestFactory1() : value("(empty)") { print_default_created(this); }
0023     explicit TestFactory1(int v) : value(std::to_string(v)) { print_created(this, value); }
0024     explicit TestFactory1(std::string v) : value(std::move(v)) { print_created(this, value); }
0025 
0026 public:
0027     std::string value;
0028     TestFactory1(TestFactory1 &&) = delete;
0029     TestFactory1(const TestFactory1 &) = delete;
0030     TestFactory1 &operator=(TestFactory1 &&) = delete;
0031     TestFactory1 &operator=(const TestFactory1 &) = delete;
0032     ~TestFactory1() { print_destroyed(this); }
0033 };
0034 // Non-public construction, but moveable:
0035 class TestFactory2 {
0036     friend class TestFactoryHelper;
0037     TestFactory2() : value("(empty2)") { print_default_created(this); }
0038     explicit TestFactory2(int v) : value(std::to_string(v)) { print_created(this, value); }
0039     explicit TestFactory2(std::string v) : value(std::move(v)) { print_created(this, value); }
0040 
0041 public:
0042     TestFactory2(TestFactory2 &&m) noexcept : value{std::move(m.value)} {
0043         print_move_created(this);
0044     }
0045     TestFactory2 &operator=(TestFactory2 &&m) noexcept {
0046         value = std::move(m.value);
0047         print_move_assigned(this);
0048         return *this;
0049     }
0050     std::string value;
0051     ~TestFactory2() { print_destroyed(this); }
0052 };
0053 // Mixed direct/factory construction:
0054 class TestFactory3 {
0055 protected:
0056     friend class TestFactoryHelper;
0057     TestFactory3() : value("(empty3)") { print_default_created(this); }
0058     explicit TestFactory3(int v) : value(std::to_string(v)) { print_created(this, value); }
0059 
0060 public:
0061     explicit TestFactory3(std::string v) : value(std::move(v)) { print_created(this, value); }
0062     TestFactory3(TestFactory3 &&m) noexcept : value{std::move(m.value)} {
0063         print_move_created(this);
0064     }
0065     TestFactory3 &operator=(TestFactory3 &&m) noexcept {
0066         value = std::move(m.value);
0067         print_move_assigned(this);
0068         return *this;
0069     }
0070     std::string value;
0071     virtual ~TestFactory3() { print_destroyed(this); }
0072 };
0073 // Inheritance test
0074 class TestFactory4 : public TestFactory3 {
0075 public:
0076     TestFactory4() : TestFactory3() { print_default_created(this); }
0077     explicit TestFactory4(int v) : TestFactory3(v) { print_created(this, v); }
0078     ~TestFactory4() override { print_destroyed(this); }
0079 };
0080 // Another class for an invalid downcast test
0081 class TestFactory5 : public TestFactory3 {
0082 public:
0083     explicit TestFactory5(int i) : TestFactory3(i) { print_created(this, i); }
0084     ~TestFactory5() override { print_destroyed(this); }
0085 };
0086 
0087 class TestFactory6 {
0088 protected:
0089     int value;
0090     bool alias = false;
0091 
0092 public:
0093     explicit TestFactory6(int i) : value{i} { print_created(this, i); }
0094     TestFactory6(TestFactory6 &&f) noexcept {
0095         print_move_created(this);
0096         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
0097         value = f.value;
0098         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
0099         alias = f.alias;
0100     }
0101     TestFactory6(const TestFactory6 &f) {
0102         print_copy_created(this);
0103         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
0104         value = f.value;
0105         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
0106         alias = f.alias;
0107     }
0108     virtual ~TestFactory6() { print_destroyed(this); }
0109     virtual int get() { return value; }
0110     bool has_alias() const { return alias; }
0111 };
0112 class PyTF6 : public TestFactory6 {
0113 public:
0114     // Special constructor that allows the factory to construct a PyTF6 from a TestFactory6 only
0115     // when an alias is needed:
0116     explicit PyTF6(TestFactory6 &&base) : TestFactory6(std::move(base)) {
0117         alias = true;
0118         print_created(this, "move", value);
0119     }
0120     explicit PyTF6(int i) : TestFactory6(i) {
0121         alias = true;
0122         print_created(this, i);
0123     }
0124     PyTF6(PyTF6 &&f) noexcept : TestFactory6(std::move(f)) { print_move_created(this); }
0125     PyTF6(const PyTF6 &f) : TestFactory6(f) { print_copy_created(this); }
0126     explicit PyTF6(std::string s) : TestFactory6((int) s.size()) {
0127         alias = true;
0128         print_created(this, s);
0129     }
0130     ~PyTF6() override { print_destroyed(this); }
0131     int get() override { PYBIND11_OVERRIDE(int, TestFactory6, get, /*no args*/); }
0132 };
0133 
0134 class TestFactory7 {
0135 protected:
0136     int value;
0137     bool alias = false;
0138 
0139 public:
0140     explicit TestFactory7(int i) : value{i} { print_created(this, i); }
0141     TestFactory7(TestFactory7 &&f) noexcept {
0142         print_move_created(this);
0143         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
0144         value = f.value;
0145         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
0146         alias = f.alias;
0147     }
0148     TestFactory7(const TestFactory7 &f) {
0149         print_copy_created(this);
0150         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
0151         value = f.value;
0152         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
0153         alias = f.alias;
0154     }
0155     virtual ~TestFactory7() { print_destroyed(this); }
0156     virtual int get() { return value; }
0157     bool has_alias() const { return alias; }
0158 };
0159 class PyTF7 : public TestFactory7 {
0160 public:
0161     explicit PyTF7(int i) : TestFactory7(i) {
0162         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
0163         alias = true;
0164         print_created(this, i);
0165     }
0166     PyTF7(PyTF7 &&f) noexcept : TestFactory7(std::move(f)) { print_move_created(this); }
0167     PyTF7(const PyTF7 &f) : TestFactory7(f) { print_copy_created(this); }
0168     ~PyTF7() override { print_destroyed(this); }
0169     int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); }
0170 };
0171 
0172 class TestFactoryHelper {
0173 public:
0174     // Non-movable, non-copyable type:
0175     // Return via pointer:
0176     static TestFactory1 *construct1() { return new TestFactory1(); }
0177     // Holder:
0178     static std::unique_ptr<TestFactory1> construct1(int a) {
0179         return std::unique_ptr<TestFactory1>(new TestFactory1(a));
0180     }
0181     // pointer again
0182     static TestFactory1 *construct1_string(std::string a) {
0183         return new TestFactory1(std::move(a));
0184     }
0185 
0186     // Moveable type:
0187     // pointer:
0188     static TestFactory2 *construct2() { return new TestFactory2(); }
0189     // holder:
0190     static std::unique_ptr<TestFactory2> construct2(int a) {
0191         return std::unique_ptr<TestFactory2>(new TestFactory2(a));
0192     }
0193     // by value moving:
0194     static TestFactory2 construct2(std::string a) { return TestFactory2(std::move(a)); }
0195 
0196     // shared_ptr holder type:
0197     // pointer:
0198     static TestFactory3 *construct3() { return new TestFactory3(); }
0199     // holder:
0200     static std::shared_ptr<TestFactory3> construct3(int a) {
0201         return std::shared_ptr<TestFactory3>(new TestFactory3(a));
0202     }
0203 };
0204 
0205 TEST_SUBMODULE(factory_constructors, m) {
0206 
0207     // Define various trivial types to allow simpler overload resolution:
0208     py::module_ m_tag = m.def_submodule("tag");
0209 #define MAKE_TAG_TYPE(Name)                                                                       \
0210     struct Name##_tag {};                                                                         \
0211     py::class_<Name##_tag>(m_tag, #Name "_tag").def(py::init<>());                                \
0212     m_tag.attr(#Name) = py::cast(Name##_tag{})
0213     MAKE_TAG_TYPE(pointer);
0214     MAKE_TAG_TYPE(unique_ptr);
0215     MAKE_TAG_TYPE(move);
0216     MAKE_TAG_TYPE(shared_ptr);
0217     MAKE_TAG_TYPE(derived);
0218     MAKE_TAG_TYPE(TF4);
0219     MAKE_TAG_TYPE(TF5);
0220     MAKE_TAG_TYPE(null_ptr);
0221     MAKE_TAG_TYPE(null_unique_ptr);
0222     MAKE_TAG_TYPE(null_shared_ptr);
0223     MAKE_TAG_TYPE(base);
0224     MAKE_TAG_TYPE(invalid_base);
0225     MAKE_TAG_TYPE(alias);
0226     MAKE_TAG_TYPE(unaliasable);
0227     MAKE_TAG_TYPE(mixed);
0228 
0229     // test_init_factory_basic, test_bad_type
0230     py::class_<TestFactory1>(m, "TestFactory1")
0231         .def(py::init([](unique_ptr_tag, int v) { return TestFactoryHelper::construct1(v); }))
0232         .def(py::init(&TestFactoryHelper::construct1_string)) // raw function pointer
0233         .def(py::init([](pointer_tag) { return TestFactoryHelper::construct1(); }))
0234         .def(py::init(
0235             [](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); }))
0236         .def_readwrite("value", &TestFactory1::value);
0237     py::class_<TestFactory2>(m, "TestFactory2")
0238         .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); }))
0239         .def(py::init([](unique_ptr_tag, std::string v) {
0240             return TestFactoryHelper::construct2(std::move(v));
0241         }))
0242         .def(py::init([](move_tag) { return TestFactoryHelper::construct2(); }))
0243         .def_readwrite("value", &TestFactory2::value);
0244 
0245     // Stateful & reused:
0246     int c = 1;
0247     auto c4a = [c](pointer_tag, TF4_tag, int a) {
0248         (void) c;
0249         return new TestFactory4(a);
0250     };
0251 
0252     // test_init_factory_basic, test_init_factory_casting
0253     py::class_<TestFactory3, std::shared_ptr<TestFactory3>> pyTestFactory3(m, "TestFactory3");
0254     pyTestFactory3
0255         .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); }))
0256         .def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); }));
0257     ignoreOldStyleInitWarnings([&pyTestFactory3]() {
0258         pyTestFactory3.def("__init__", [](TestFactory3 &self, std::string v) {
0259             new (&self) TestFactory3(std::move(v));
0260         }); // placement-new ctor
0261     });
0262     pyTestFactory3
0263         // factories returning a derived type:
0264         .def(py::init(c4a)) // derived ptr
0265         .def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); }))
0266         // derived shared ptr:
0267         .def(py::init(
0268             [](shared_ptr_tag, TF4_tag, int a) { return std::make_shared<TestFactory4>(a); }))
0269         .def(py::init(
0270             [](shared_ptr_tag, TF5_tag, int a) { return std::make_shared<TestFactory5>(a); }))
0271 
0272         // Returns nullptr:
0273         .def(py::init([](null_ptr_tag) { return (TestFactory3 *) nullptr; }))
0274         .def(py::init([](null_unique_ptr_tag) { return std::unique_ptr<TestFactory3>(); }))
0275         .def(py::init([](null_shared_ptr_tag) { return std::shared_ptr<TestFactory3>(); }))
0276 
0277         .def_readwrite("value", &TestFactory3::value);
0278 
0279     // test_init_factory_casting
0280     py::class_<TestFactory4, TestFactory3, std::shared_ptr<TestFactory4>>(m, "TestFactory4")
0281         .def(py::init(c4a)) // pointer
0282         ;
0283 
0284     // Doesn't need to be registered, but registering makes getting ConstructorStats easier:
0285     py::class_<TestFactory5, TestFactory3, std::shared_ptr<TestFactory5>>(m, "TestFactory5");
0286 
0287     // test_init_factory_alias
0288     // Alias testing
0289     py::class_<TestFactory6, PyTF6>(m, "TestFactory6")
0290         .def(py::init([](base_tag, int i) { return TestFactory6(i); }))
0291         .def(py::init([](alias_tag, int i) { return PyTF6(i); }))
0292         .def(py::init([](alias_tag, std::string s) { return PyTF6(std::move(s)); }))
0293         .def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF6(i); }))
0294         .def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory6(i); }))
0295         .def(py::init(
0296             [](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); }))
0297 
0298         .def("get", &TestFactory6::get)
0299         .def("has_alias", &TestFactory6::has_alias)
0300 
0301         .def_static(
0302             "get_cstats", &ConstructorStats::get<TestFactory6>, py::return_value_policy::reference)
0303         .def_static(
0304             "get_alias_cstats", &ConstructorStats::get<PyTF6>, py::return_value_policy::reference);
0305 
0306     // test_init_factory_dual
0307     // Separate alias constructor testing
0308     py::class_<TestFactory7, PyTF7, std::shared_ptr<TestFactory7>>(m, "TestFactory7")
0309         .def(py::init([](int i) { return TestFactory7(i); }, [](int i) { return PyTF7(i); }))
0310         .def(py::init([](pointer_tag, int i) { return new TestFactory7(i); },
0311                       [](pointer_tag, int i) { return new PyTF7(i); }))
0312         .def(py::init([](mixed_tag, int i) { return new TestFactory7(i); },
0313                       [](mixed_tag, int i) { return PyTF7(i); }))
0314         .def(py::init([](mixed_tag, const std::string &s) { return TestFactory7((int) s.size()); },
0315                       [](mixed_tag, const std::string &s) { return new PyTF7((int) s.size()); }))
0316         .def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory7(i); },
0317                       [](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); }))
0318         .def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF7(i); },
0319                       [](alias_tag, pointer_tag, int i) { return new PyTF7(10 * i); }))
0320         .def(py::init(
0321             [](shared_ptr_tag, base_tag, int i) { return std::make_shared<TestFactory7>(i); },
0322             [](shared_ptr_tag, base_tag, int i) {
0323                 auto *p = new PyTF7(i);
0324                 return std::shared_ptr<TestFactory7>(p);
0325             }))
0326         .def(py::init([](shared_ptr_tag,
0327                          invalid_base_tag,
0328                          int i) { return std::make_shared<TestFactory7>(i); },
0329                       [](shared_ptr_tag, invalid_base_tag, int i) {
0330                           return std::make_shared<TestFactory7>(i);
0331                       })) // <-- invalid alias factory
0332 
0333         .def("get", &TestFactory7::get)
0334         .def("has_alias", &TestFactory7::has_alias)
0335 
0336         .def_static(
0337             "get_cstats", &ConstructorStats::get<TestFactory7>, py::return_value_policy::reference)
0338         .def_static(
0339             "get_alias_cstats", &ConstructorStats::get<PyTF7>, py::return_value_policy::reference);
0340 
0341     // test_placement_new_alternative
0342     // Class with a custom new operator but *without* a placement new operator (issue #948)
0343     class NoPlacementNew {
0344     public:
0345         explicit NoPlacementNew(int i) : i(i) {}
0346         static void *operator new(std::size_t s) {
0347             auto *p = ::operator new(s);
0348             py::print("operator new called, returning", reinterpret_cast<uintptr_t>(p));
0349             return p;
0350         }
0351         static void operator delete(void *p) {
0352             py::print("operator delete called on", reinterpret_cast<uintptr_t>(p));
0353             ::operator delete(p);
0354         }
0355         int i;
0356     };
0357     // As of 2.2, `py::init<args>` no longer requires placement new
0358     py::class_<NoPlacementNew>(m, "NoPlacementNew")
0359         .def(py::init<int>())
0360         .def(py::init([]() { return new NoPlacementNew(100); }))
0361         .def_readwrite("i", &NoPlacementNew::i);
0362 
0363     // test_reallocations
0364     // Class that has verbose operator_new/operator_delete calls
0365     struct NoisyAlloc {
0366         NoisyAlloc(const NoisyAlloc &) = default;
0367         explicit NoisyAlloc(int i) { py::print(py::str("NoisyAlloc(int {})").format(i)); }
0368         explicit NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); }
0369         ~NoisyAlloc() { py::print("~NoisyAlloc()"); }
0370 
0371         static void *operator new(size_t s) {
0372             py::print("noisy new");
0373             return ::operator new(s);
0374         }
0375         static void *operator new(size_t, void *p) {
0376             py::print("noisy placement new");
0377             return p;
0378         }
0379         static void operator delete(void *p, size_t) {
0380             py::print("noisy delete");
0381             ::operator delete(p);
0382         }
0383         static void operator delete(void *, void *) { py::print("noisy placement delete"); }
0384     };
0385 
0386     py::class_<NoisyAlloc> pyNoisyAlloc(m, "NoisyAlloc");
0387     // Since these overloads have the same number of arguments, the dispatcher will try each of
0388     // them until the arguments convert.  Thus we can get a pre-allocation here when passing a
0389     // single non-integer:
0390     ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
0391         pyNoisyAlloc.def("__init__", [](NoisyAlloc *a, int i) {
0392             new (a) NoisyAlloc(i);
0393         }); // Regular constructor, runs first, requires preallocation
0394     });
0395 
0396     pyNoisyAlloc.def(py::init([](double d) { return new NoisyAlloc(d); }));
0397 
0398     // The two-argument version: first the factory pointer overload.
0399     pyNoisyAlloc.def(py::init([](int i, int) { return new NoisyAlloc(i); }));
0400     // Return-by-value:
0401     pyNoisyAlloc.def(py::init([](double d, int) { return NoisyAlloc(d); }));
0402     // Old-style placement new init; requires preallocation
0403     ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
0404         pyNoisyAlloc.def("__init__",
0405                          [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); });
0406     });
0407     // Requires deallocation of previous overload preallocated value:
0408     pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); }));
0409     // Regular again: requires yet another preallocation
0410     ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
0411         pyNoisyAlloc.def(
0412             "__init__", [](NoisyAlloc &a, int i, const std::string &) { new (&a) NoisyAlloc(i); });
0413     });
0414 
0415     // static_assert testing (the following def's should all fail with appropriate compilation
0416     // errors):
0417 #if 0
0418     struct BadF1Base {};
0419     struct BadF1 : BadF1Base {};
0420     struct PyBadF1 : BadF1 {};
0421     py::class_<BadF1, PyBadF1, std::shared_ptr<BadF1>> bf1(m, "BadF1");
0422     // wrapped factory function must return a compatible pointer, holder, or value
0423     bf1.def(py::init([]() { return 3; }));
0424     // incompatible factory function pointer return type
0425     bf1.def(py::init([]() { static int three = 3; return &three; }));
0426     // incompatible factory function std::shared_ptr<T> return type: cannot convert shared_ptr<T> to holder
0427     // (non-polymorphic base)
0428     bf1.def(py::init([]() { return std::shared_ptr<BadF1Base>(new BadF1()); }));
0429 #endif
0430 }