Skip to content

Commit 416bbe9

Browse files
committed
support for brace initialization
1 parent 919b9dd commit 416bbe9

File tree

4 files changed

+47
-5
lines changed

4 files changed

+47
-5
lines changed

docs/advanced/classes.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,30 @@ you could equivalently write:
492492
493493
which will invoke the constructor in-place at the pre-allocated memory.
494494

495+
Brace initialization
496+
--------------------
497+
498+
``pybind11::init<>`` internally uses C++11 brace initialization to call the
499+
constructor of the target class. This means that it can be used to bind
500+
*implicit* constructors as well:
501+
502+
.. code-block:: cpp
503+
504+
struct Aggregate {
505+
int a;
506+
std::string b;
507+
};
508+
509+
py::class_<Aggregate>(m, "Aggregate")
510+
.def(py::init<int, const std::string &>());
511+
512+
.. note::
513+
514+
Note that brace initialization preferentially invokes constructor overloads
515+
taking a ``std::initializer_list``. In the rare event that this causes an
516+
issue, you can work around it by using ``py::init(...)`` with a lambda
517+
function that constructs the new object as desired.
518+
495519
.. _classes_with_non_public_destructors:
496520

497521
Non-public destructors

include/pybind11/detail/init.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ template <typename... Args> struct constructor {
172172
// we really can't support that in C++, so just ignore the second __init__.
173173
if (v_h.instance_registered()) return;
174174

175-
construct<Class>(v_h, new Cpp<Class>(std::forward<Args>(args)...), false);
175+
construct<Class>(v_h, new Cpp<Class>{std::forward<Args>(args)...}, false);
176176
}, extra...);
177177
}
178178

@@ -186,9 +186,9 @@ template <typename... Args> struct constructor {
186186
if (v_h.instance_registered()) return; // Ignore duplicate __init__ calls (see above)
187187

188188
if (Py_TYPE(v_h.inst) == cl_type->type)
189-
construct<Class>(v_h, new Cpp<Class>(std::forward<Args>(args)...), false);
189+
construct<Class>(v_h, new Cpp<Class>{std::forward<Args>(args)...}, false);
190190
else
191-
construct<Class>(v_h, new Alias<Class>(std::forward<Args>(args)...), true);
191+
construct<Class>(v_h, new Alias<Class>{std::forward<Args>(args)...}, true);
192192
}, extra...);
193193
}
194194

@@ -200,7 +200,7 @@ template <typename... Args> struct constructor {
200200
cl.def("__init__", [cl_type](handle self_, Args... args) {
201201
auto v_h = load_v_h(self_, cl_type);
202202
if (v_h.instance_registered()) return; // Ignore duplicate __init__ calls (see above)
203-
construct<Class>(v_h, new Alias<Class>(std::forward<Args>(args)...), true);
203+
construct<Class>(v_h, new Alias<Class>{std::forward<Args>(args)...}, true);
204204
}, extra...);
205205
}
206206
};
@@ -214,7 +214,7 @@ template <typename... Args> struct alias_constructor {
214214
cl.def("__init__", [cl_type](handle self_, Args... args) {
215215
auto v_h = load_v_h(self_, cl_type);
216216
if (v_h.instance_registered()) return; // Ignore duplicate __init__ calls (see above)
217-
construct<Class>(v_h, new Alias<Class>(std::forward<Args>(args)...), true);
217+
construct<Class>(v_h, new Alias<Class>{std::forward<Args>(args)...}, true);
218218
}, extra...);
219219
}
220220
};

tests/test_class.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,17 @@ TEST_SUBMODULE(class_, m) {
280280
#else
281281
.def("foo", static_cast<int (ProtectedB::*)() const>(&PublicistB::foo));
282282
#endif
283+
284+
// test_brace_initialization
285+
struct BraceInitialization {
286+
int field1;
287+
std::string field2;
288+
};
289+
290+
py::class_<BraceInitialization>(m, "BraceInitialization")
291+
.def(py::init<int, const std::string &>())
292+
.def_readwrite("field1", &BraceInitialization::field1)
293+
.def_readwrite("field2", &BraceInitialization::field2);
283294
}
284295

285296
template <int N> class BreaksBase { public: virtual ~BreaksBase() = default; };

tests/test_class.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,10 @@ def foo(self):
195195

196196
c = C()
197197
assert c.foo() == 0
198+
199+
200+
def test_brace_initialization():
201+
""" Tests that simple POD classes can be constructed using C++11 brace initialization """
202+
a = m.BraceInitialization(123, "test")
203+
assert a.field1 == 123
204+
assert a.field2 == "test"

0 commit comments

Comments
 (0)