diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index e2b63757d7..481b920972 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -11,6 +11,7 @@ #include "detail/common.h" #include "buffer_info.h" +#include #include #include @@ -1213,9 +1214,21 @@ class capsule : public object { class tuple : public object { public: PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) + /** \rst + Creates an empty ``tuple`` with given ``size`` to be later filled in. + \endrst */ explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); } + /** \rst + Creates a ``tuple`` from an initializer list of object instances. + \endrst */ + explicit tuple(std::initializer_list init_list) : tuple(init_list.size()) { + detail::accessor_policies::tuple_item::key_type index{0}; + for (const pybind11::object& item : init_list) + detail::tuple_accessor(*this, index++) = item; + } + size_t size() const { return (size_t) PyTuple_Size(m_ptr); } bool empty() const { return size() == 0; } detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } @@ -1276,6 +1289,12 @@ class list : public object { explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { if (!m_ptr) pybind11_fail("Could not allocate list object!"); } + explicit list(std::initializer_list init_list) : list(init_list.size()) { + detail::accessor_policies::list_item::key_type index {0}; + for (const pybind11::object& item : init_list) + detail::list_accessor(*this, index++) = item; + } + size_t size() const { return (size_t) PyList_Size(m_ptr); } bool empty() const { return size() == 0; } detail::list_accessor operator[](size_t index) const { return {*this, index}; } @@ -1300,6 +1319,11 @@ class set : public object { set() : object(PySet_New(nullptr), stolen_t{}) { if (!m_ptr) pybind11_fail("Could not allocate set object!"); } + explicit set(std::initializer_list init_list): set() { + for (const object& item : init_list) + PySet_Add(m_ptr, item.ptr()); + } + size_t size() const { return (size_t) PySet_Size(m_ptr); } bool empty() const { return size() == 0; } template bool add(T &&val) const { diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 9dae6e7d62..ba2c420c91 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -9,6 +9,13 @@ #include "pybind11_tests.h" +#include +#include +#include +#include +#include +#include + TEST_SUBMODULE(pytypes, m) { // test_int @@ -237,6 +244,39 @@ TEST_SUBMODULE(pytypes, m) { ); }); + // Some python containers can also be constructed using an initializer list + m.def("initializer_list", []() { + return py::dict( + "tuple_ints"_a = py::tuple{py::int_(1), py::int_(2), py::int_(3)}, + "tuple_floats"_a = py::tuple{py::float_(2.2), py::float_(3.1), py::float_(4.5), py::float_(5.4)}, + "list_ints"_a = py::list{py::int_(1), py::int_(2), py::int_(3)}, + "list_floats"_a = py::list{py::float_(2.2), py::float_(3.1), py::float_(4.5), py::float_(5.4)}, + "tuple_objects"_a = py::tuple{ + py::int_(321), + py::float_(123.3), + "somestring"_s, + py::tuple({py::int_(1), py::float_(3.3)}), + py::list({py::float_(5.5), py::int_(12)}), + py::dict("k_s"_a="v1", "k_i"_a=12, "k_f"_a=12.1) + }, + "list_objects"_a = py::list{ + py::int_(321), + py::float_(123.3), + "somestring"_s, + py::tuple({py::int_(1), py::float_(3.3)}), + py::list({py::float_(5.5), py::int_(12)}), + py::dict("k_s"_a="v1", "k_i"_a=12, "k_f"_a=12.1) + }, + // Tuples are hashable, lists and dicts are not + "set_objects"_a = py::set{ + py::int_(321), + py::float_(123.3), + "somestring"_s, + py::tuple({py::int_(1), py::float_(3.3)}) + } + ); + }); + m.def("convert_to_pybind11_str", [](py::object o) { return py::str(o); }); m.def("get_implicit_casting", []() { diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index c21ad61146..f6ce629038 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -221,6 +221,20 @@ def test_constructors(): for k in noconv2: assert noconv2[k] is expected[k] + init_list = m.initializer_list() + assert init_list["tuple_ints"] == (1, 2, 3) + assert init_list["tuple_floats"] == (2.2, 3.1, 4.5, 5.4) + assert init_list["list_ints"] == [1, 2, 3] + assert init_list["list_floats"] == [2.2, 3.1, 4.5, 5.4] + + assert init_list["tuple_objects"] == ( + 321, 123.3, "somestring", (1, 3.3), [5.5, 12], {"k_s": "v1", "k_i": 12, "k_f": 12.1} + ) + assert init_list["list_objects"] == [ + 321, 123.3, "somestring", (1, 3.3), [5.5, 12], {"k_s": "v1", "k_i": 12, "k_f": 12.1} + ] + assert init_list["set_objects"] == {321, 123.3, "somestring", (1, 3.3)} + def test_pybind11_str_raw_str(): # specifically to exercise pybind11::str::raw_str