From 4336a7da4a890ba421da72d7d0c48c7d7f276a50 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 21 Aug 2017 22:48:28 +0200 Subject: [PATCH] support for brace initialization --- docs/advanced/classes.rst | 24 ++++++++++++++++++++++++ include/pybind11/detail/init.h | 10 +++++----- tests/test_class.cpp | 11 +++++++++++ tests/test_class.py | 7 +++++++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index 4550d0b98..8926fa928 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -492,6 +492,30 @@ you could equivalently write: which will invoke the constructor in-place at the pre-allocated memory. +Brace initialization +-------------------- + +``pybind11::init<>`` internally uses C++11 brace initialization to call the +constructor of the target class. This means that it can be used to bind +*implicit* constructors as well: + +.. code-block:: cpp + + struct Aggregate { + int a; + std::string b; + }; + + py::class_(m, "Aggregate") + .def(py::init()); + +.. note:: + + Note that brace initialization preferentially invokes constructor overloads + taking a ``std::initializer_list``. In the rare event that this causes an + issue, you can work around it by using ``py::init(...)`` with a lambda + function that constructs the new object as desired. + .. _classes_with_non_public_destructors: Non-public destructors diff --git a/include/pybind11/detail/init.h b/include/pybind11/detail/init.h index e98adbb26..647279cf0 100644 --- a/include/pybind11/detail/init.h +++ b/include/pybind11/detail/init.h @@ -172,7 +172,7 @@ template struct constructor { // we really can't support that in C++, so just ignore the second __init__. if (v_h.instance_registered()) return; - construct(v_h, new Cpp(std::forward(args)...), false); + construct(v_h, new Cpp{std::forward(args)...}, false); }, extra...); } @@ -186,9 +186,9 @@ template struct constructor { if (v_h.instance_registered()) return; // Ignore duplicate __init__ calls (see above) if (Py_TYPE(v_h.inst) == cl_type->type) - construct(v_h, new Cpp(std::forward(args)...), false); + construct(v_h, new Cpp{std::forward(args)...}, false); else - construct(v_h, new Alias(std::forward(args)...), true); + construct(v_h, new Alias{std::forward(args)...}, true); }, extra...); } @@ -200,7 +200,7 @@ template struct constructor { cl.def("__init__", [cl_type](handle self_, Args... args) { auto v_h = load_v_h(self_, cl_type); if (v_h.instance_registered()) return; // Ignore duplicate __init__ calls (see above) - construct(v_h, new Alias(std::forward(args)...), true); + construct(v_h, new Alias{std::forward(args)...}, true); }, extra...); } }; @@ -214,7 +214,7 @@ template struct alias_constructor { cl.def("__init__", [cl_type](handle self_, Args... args) { auto v_h = load_v_h(self_, cl_type); if (v_h.instance_registered()) return; // Ignore duplicate __init__ calls (see above) - construct(v_h, new Alias(std::forward(args)...), true); + construct(v_h, new Alias{std::forward(args)...}, true); }, extra...); } }; diff --git a/tests/test_class.cpp b/tests/test_class.cpp index af7b0bfb0..842991aaf 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -280,6 +280,17 @@ TEST_SUBMODULE(class_, m) { #else .def("foo", static_cast(&PublicistB::foo)); #endif + + // test_brace_initialization + struct BraceInitialization { + int field1; + std::string field2; + }; + + py::class_(m, "BraceInitialization") + .def(py::init()) + .def_readwrite("field1", &BraceInitialization::field1) + .def_readwrite("field2", &BraceInitialization::field2); } template class BreaksBase { public: virtual ~BreaksBase() = default; }; diff --git a/tests/test_class.py b/tests/test_class.py index c8ff85735..b2cc27575 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -195,3 +195,10 @@ def test_bind_protected_functions(): c = C() assert c.foo() == 0 + + +def test_brace_initialization(): + """ Tests that simple POD classes can be constructed using C++11 brace initialization """ + a = m.BraceInitialization(123, "test") + assert a.field1 == 123 + assert a.field2 == "test"