From 593047ecaf68b93d1892d3ff12bccd434ee4b47e Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Tue, 1 Sep 2020 16:36:38 +0200 Subject: [PATCH 01/11] pybind11::tuple and pybind11::list can be constructed using a std::initializer_list (see #1669) --- include/pybind11/pytypes.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index e2b63757d7..de1424a947 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 @@ -1216,6 +1217,11 @@ class tuple : public object { explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); } + explicit tuple(std::initializer_list init_list) : tuple(init_list.size()) { + size_t 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 +1282,11 @@ 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()) { + size_t 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}; } From 586b9e51890595f5f569ce9d5baadb95a79e7029 Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Tue, 1 Sep 2020 16:38:27 +0200 Subject: [PATCH 02/11] Test initializer-list based constructors for tuple and list --- tests/test_pytypes.cpp | 10 ++++++++++ tests/test_pytypes.py | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 9dae6e7d62..b37f5900c0 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -237,6 +237,16 @@ TEST_SUBMODULE(pytypes, m) { ); }); + // Tuples and lists can also be constructed using an initializer list of pybind11::object subclasses + 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)}) + ); + }); + 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..33122b0775 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -221,6 +221,26 @@ def test_constructors(): for k in noconv2: assert noconv2[k] is expected[k] + init_list = m.initializer_list() + expected = ("tuple_ints", tuple), ("list_ints", list) + for key, type_ in expected: + ints = init_list[key] + assert(isinstance(ints, type_)) + assert(len(ints) == 3) + assert(ints[0] == 1) + assert(ints[1] == 2) + assert(ints[2] == 3) + + expected = ("tuple_floats", tuple), ("list_floats", list) + for key, type_ in expected: + floats = init_list[key] + assert(isinstance(floats, type_)) + assert(len(floats) == 4) + assert(floats[0] == 2.2) + assert(floats[1] == 3.1) + assert(floats[2] == 4.5) + assert(floats[3] == 5.4) + def test_pybind11_str_raw_str(): # specifically to exercise pybind11::str::raw_str From 8e33d264c355ef45a8162f8f5d1716a5926566b2 Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Wed, 2 Sep 2020 09:46:06 +0200 Subject: [PATCH 03/11] Test case creates a tuple and a list with mixed datatypes using initializer list. --- tests/test_pytypes.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index b37f5900c0..7475b7247b 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -243,7 +243,21 @@ TEST_SUBMODULE(pytypes, m) { "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)}) + "list_floats"_a = py::list({py::float_(2.2), py::float_(3.1), py::float_(4.5), py::float_(5.4)}), + "tuple_mixed"_a = py::tuple({ + py::int_(321), + py::float_(123.3), + 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_mixed"_a = py::list({ + py::int_(321), + py::float_(123.3), + 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), + }) ); }); From c9ac962f4de4298fdc9db4905c7a4f7f2991f69a Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Wed, 2 Sep 2020 09:47:13 +0200 Subject: [PATCH 04/11] Refactored test_pytypes to account for #2451 suggestions. test_pytypes also checks for new mixed types data returned. --- tests/test_pytypes.py | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 33122b0775..3c6ca8e608 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -222,24 +222,16 @@ def test_constructors(): assert noconv2[k] is expected[k] init_list = m.initializer_list() - expected = ("tuple_ints", tuple), ("list_ints", list) - for key, type_ in expected: - ints = init_list[key] - assert(isinstance(ints, type_)) - assert(len(ints) == 3) - assert(ints[0] == 1) - assert(ints[1] == 2) - assert(ints[2] == 3) - - expected = ("tuple_floats", tuple), ("list_floats", list) - for key, type_ in expected: - floats = init_list[key] - assert(isinstance(floats, type_)) - assert(len(floats) == 4) - assert(floats[0] == 2.2) - assert(floats[1] == 3.1) - assert(floats[2] == 4.5) - assert(floats[3] == 5.4) + assert init_list["tuple_ints"] == (1, 2, 3) + assert init_list["list_ints"] == [1, 2, 3] + assert init_list["tuple_floats"] == (2.2, 3.1, 4.5, 5.4) + assert init_list["list_floats"] == [2.2, 3.1, 4.5, 5.4] + assert init_list["tuple_mixed"] == ( + 321, 123.3, (1, 3.3), [5.5, 12], {"k_s": "v1", "k_i": 12, "k_f": 12.1} + ) + assert init_list["list_mixed"] == [ + 321, 123.3, (1, 3.3), [5.5, 12], {"k_s": "v1", "k_i": 12, "k_f": 12.1} + ] def test_pybind11_str_raw_str(): From 81128c8e88ea25bbb434bbad5000575fd80b4424 Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Thu, 3 Sep 2020 12:52:12 +0200 Subject: [PATCH 05/11] Work in progress extension of the initializer-list initialization concept (only tuple right now). Provides 3 new constructors: initializer_list to use heterogeneous py::objects initializer_list to use homogeneous C++ objects container to use C++ containers that provide a size() method and a forward iterator (see #2451 discussion) --- include/pybind11/pytypes.h | 60 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index de1424a947..67d902f29a 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -15,6 +15,8 @@ #include #include +#include + PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) /* A few forward declarations */ @@ -1214,14 +1216,64 @@ 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!"); } - explicit tuple(std::initializer_list init_list) : tuple(init_list.size()) { - size_t index {0}; + + /** \rst + Creates a ``tuple`` from an initializer list of object instances. + + The second argument is unused and serves the purpose of having a different + signature from the other std::initializer_list based constructor. + \endrst */ + // The templated-based initializer_list constructor does not work with an init + // list composed of heterogeneour pybind11::objects so this specific one was added. + explicit tuple(std::initializer_list init_list, size_t index = 0) : tuple(init_list.size()) { + index = 0; for (const pybind11::object& item : init_list) detail::tuple_accessor(*this, index++) = item; } + + /** \rst + Creates a ``tuple`` from an initializer list of C++ objects. + + Note that there must be a pybind11 cast available to convert the + C++ object to a pybind11::object. + \endrst */ + template + explicit tuple(std::initializer_list init_list) : tuple(init_list.size()) { + size_t index{0}; + for (const T& item : init_list) { + detail::tuple_accessor(*this, index++) = item; + } + } + + /** \rst + Creates a ``tuple`` from an arbitrary C++ container of known size. + + Note that there must be a pybind11 cast available to convert the + C++ object to a pybind11::object. + \endrst */ + template< + typename T, + typename = std::enable_if< + std::is_base_of< + std::forward_iterator_tag, + typename std::iterator_traits::iterator_category + >::value && + std::is_unsigned::value + > + > + explicit tuple(T&& init_list) : tuple(init_list.size()) { + size_t index{0}; + for (const typename T::value_type& 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}; } @@ -1311,6 +1363,10 @@ class set : public object { set() : object(PySet_New(nullptr), stolen_t{}) { if (!m_ptr) pybind11_fail("Could not allocate set object!"); } + 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 { From 4d8df0d1051567f507ede69a2bd9254cb0a21c86 Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Thu, 3 Sep 2020 12:57:03 +0200 Subject: [PATCH 06/11] Expanded the initializer_list constructor for tuple, list and set tests using also py::str. Added tests for WIP container based constructor for tuple. --- tests/test_pytypes.cpp | 53 +++++++++++++++++++++++++++++++++--------- tests/test_pytypes.py | 22 +++++++++++++----- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 7475b7247b..66f1fdbdbc 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,27 +244,51 @@ TEST_SUBMODULE(pytypes, m) { ); }); - // Tuples and lists can also be constructed using an initializer list of pybind11::object subclasses + // 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_mixed"_a = py::tuple({ + "tuple_ints"_a = py::tuple{1, 2, 3}, + "tuple_floats"_a = py::tuple{2.2, 3.1, 4.5, 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_mixed"_a = py::list({ + py::dict("k_s"_a="v1", "k_i"_a=12, "k_f"_a=12.1) + }, + "list_mixed"_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), - }) + py::dict("k_s"_a="v1", "k_i"_a=12, "k_f"_a=12.1) + }, + // Tuples are hashable, lists and dicts are not + "set_mixed"_a = py::set{ + py::int_(321), + py::float_(123.3), + "somestring"_s, + py::tuple({py::int_(1), py::float_(3.3)}) + } + ); + }); + + // Some python containers can also be constructed using C++ containers + m.def("init_containers", []() { + return py::dict( + "tuple_array_ints"_a = py::tuple(std::array{1, 2, 3}), + "tuple_deque_floats"_a = py::tuple(std::deque{2.2f, 3.1f, 4.5f}), + "tuple_list_floats"_a = py::tuple(std::list{2.2f, 3.1f, 4.5f}), + "tuple_vector_ints"_a = py::tuple(std::vector{1, 2, 3}), + + "tuple_multiset_int"_a = py::tuple(std::multiset{1, 1, 2, 3}), + // std::set is sorted so insertion order does not matter. Iteration order will be + // one three two + "tuple_set_strings"_a = py::tuple(std::set{"one", "two", "three"}) ); }); diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 3c6ca8e608..f6d59dc0c7 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -224,15 +224,25 @@ def test_constructors(): init_list = m.initializer_list() assert init_list["tuple_ints"] == (1, 2, 3) assert init_list["list_ints"] == [1, 2, 3] - assert init_list["tuple_floats"] == (2.2, 3.1, 4.5, 5.4) - assert init_list["list_floats"] == [2.2, 3.1, 4.5, 5.4] - assert init_list["tuple_mixed"] == ( - 321, 123.3, (1, 3.3), [5.5, 12], {"k_s": "v1", "k_i": 12, "k_f": 12.1} + # This has been created with floats so we don't directly check with doubles + assert tuple(round(x, 1) for x in init_list["tuple_floats"]) == (2.2, 3.1, 4.5, 5.4) + assert [round(x, 1) for x in 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_mixed"] == [ - 321, 123.3, (1, 3.3), [5.5, 12], {"k_s": "v1", "k_i": 12, "k_f": 12.1} + 321, 123.3, "somestring", (1, 3.3), [5.5, 12], {"k_s": "v1", "k_i": 12, "k_f": 12.1} ] - + assert init_list["set_mixed"] == {321, 123.3, "somestring", (1, 3.3)} + + init_containers = m.init_containers() + assert init_containers["tuple_array_ints"] == (1, 2, 3) + # See above, created with floats + assert tuple((round(x, 1) for x in init_containers["tuple_deque_floats"])) == (2.2, 3.1, 4.5) + assert tuple((round(x, 1) for x in init_containers["tuple_list_floats"])) == (2.2, 3.1, 4.5) + assert init_containers["tuple_vector_ints"] == (1, 2, 3) + assert init_containers["tuple_multiset_int"] == (1, 1, 2, 3) + assert init_containers["tuple_set_strings"] == ("one", "three", "two") def test_pybind11_str_raw_str(): # specifically to exercise pybind11::str::raw_str From bb3dfc194fb5dd25fdea709da539b6847ca8c09f Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Thu, 3 Sep 2020 12:59:34 +0200 Subject: [PATCH 07/11] Removed useless include. --- include/pybind11/pytypes.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 67d902f29a..dc5bc95084 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -15,8 +15,6 @@ #include #include -#include - PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) /* A few forward declarations */ @@ -1230,7 +1228,7 @@ class tuple : public object { signature from the other std::initializer_list based constructor. \endrst */ // The templated-based initializer_list constructor does not work with an init - // list composed of heterogeneour pybind11::objects so this specific one was added. + // list composed of heterogeneous pybind11::objects so this specific one was added. explicit tuple(std::initializer_list init_list, size_t index = 0) : tuple(init_list.size()) { index = 0; for (const pybind11::object& item : init_list) From d2b21d625aea04961912f053a99eb7fc7c6040bc Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Thu, 3 Sep 2020 13:01:08 +0200 Subject: [PATCH 08/11] Added double-line separator as per flake complaints. --- tests/test_pytypes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index f6d59dc0c7..a2253b65ac 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -244,6 +244,7 @@ def test_constructors(): assert init_containers["tuple_multiset_int"] == (1, 1, 2, 3) assert init_containers["tuple_set_strings"] == ("one", "three", "two") + def test_pybind11_str_raw_str(): # specifically to exercise pybind11::str::raw_str cvt = m.convert_to_pybind11_str From c6665f242172d12b7df7c3fd2e519ce04bbe2431 Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Thu, 3 Sep 2020 15:03:45 +0200 Subject: [PATCH 09/11] Added round braces in subobject initialization (clang warning). --- tests/test_pytypes.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 66f1fdbdbc..da35847436 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -280,15 +280,15 @@ TEST_SUBMODULE(pytypes, m) { // Some python containers can also be constructed using C++ containers m.def("init_containers", []() { return py::dict( - "tuple_array_ints"_a = py::tuple(std::array{1, 2, 3}), - "tuple_deque_floats"_a = py::tuple(std::deque{2.2f, 3.1f, 4.5f}), - "tuple_list_floats"_a = py::tuple(std::list{2.2f, 3.1f, 4.5f}), - "tuple_vector_ints"_a = py::tuple(std::vector{1, 2, 3}), + "tuple_array_ints"_a = py::tuple(std::array({1, 2, 3})), + "tuple_deque_floats"_a = py::tuple(std::deque({2.2f, 3.1f, 4.5f})), + "tuple_list_floats"_a = py::tuple(std::list({2.2f, 3.1f, 4.5f})), + "tuple_vector_ints"_a = py::tuple(std::vector({1, 2, 3})), - "tuple_multiset_int"_a = py::tuple(std::multiset{1, 1, 2, 3}), + "tuple_multiset_int"_a = py::tuple(std::multiset({1, 1, 2, 3})), // std::set is sorted so insertion order does not matter. Iteration order will be // one three two - "tuple_set_strings"_a = py::tuple(std::set{"one", "two", "three"}) + "tuple_set_strings"_a = py::tuple(std::set({"one", "two", "three"})) ); }); From 5a943d88f9317c4070d5a80b31ff815c95ed090b Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Thu, 8 Oct 2020 11:38:15 +0200 Subject: [PATCH 10/11] tuple, list and set have a new std::initializer_list based constructor only. Removed all other experimental constructors. --- include/pybind11/pytypes.h | 53 +++++--------------------------------- 1 file changed, 6 insertions(+), 47 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index dc5bc95084..481b920972 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1220,58 +1220,15 @@ class tuple : public object { 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. - - The second argument is unused and serves the purpose of having a different - signature from the other std::initializer_list based constructor. \endrst */ - // The templated-based initializer_list constructor does not work with an init - // list composed of heterogeneous pybind11::objects so this specific one was added. - explicit tuple(std::initializer_list init_list, size_t index = 0) : tuple(init_list.size()) { - index = 0; + 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; } - /** \rst - Creates a ``tuple`` from an initializer list of C++ objects. - - Note that there must be a pybind11 cast available to convert the - C++ object to a pybind11::object. - \endrst */ - template - explicit tuple(std::initializer_list init_list) : tuple(init_list.size()) { - size_t index{0}; - for (const T& item : init_list) { - detail::tuple_accessor(*this, index++) = item; - } - } - - /** \rst - Creates a ``tuple`` from an arbitrary C++ container of known size. - - Note that there must be a pybind11 cast available to convert the - C++ object to a pybind11::object. - \endrst */ - template< - typename T, - typename = std::enable_if< - std::is_base_of< - std::forward_iterator_tag, - typename std::iterator_traits::iterator_category - >::value && - std::is_unsigned::value - > - > - explicit tuple(T&& init_list) : tuple(init_list.size()) { - size_t index{0}; - for (const typename T::value_type& 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}; } @@ -1333,10 +1290,11 @@ class list : public object { if (!m_ptr) pybind11_fail("Could not allocate list object!"); } explicit list(std::initializer_list init_list) : list(init_list.size()) { - size_t index {0}; + 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}; } @@ -1361,10 +1319,11 @@ class set : public object { set() : object(PySet_New(nullptr), stolen_t{}) { if (!m_ptr) pybind11_fail("Could not allocate set object!"); } - set(std::initializer_list init_list): set() { + 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 { From 27eeb9978f50b5f8a7f340bb57a8485c5ebe2957 Mon Sep 17 00:00:00 2001 From: Giuseppe Corbelli Date: Thu, 8 Oct 2020 11:39:45 +0200 Subject: [PATCH 11/11] Test that tuple, list and set std::initializer_list constructors work as expected. --- tests/test_pytypes.cpp | 23 ++++------------------- tests/test_pytypes.py | 19 +++++-------------- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index da35847436..ba2c420c91 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -247,8 +247,8 @@ 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{1, 2, 3}, - "tuple_floats"_a = py::tuple{2.2, 3.1, 4.5, 5.4}, + "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{ @@ -259,7 +259,7 @@ TEST_SUBMODULE(pytypes, m) { 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_mixed"_a = py::list{ + "list_objects"_a = py::list{ py::int_(321), py::float_(123.3), "somestring"_s, @@ -268,7 +268,7 @@ TEST_SUBMODULE(pytypes, m) { py::dict("k_s"_a="v1", "k_i"_a=12, "k_f"_a=12.1) }, // Tuples are hashable, lists and dicts are not - "set_mixed"_a = py::set{ + "set_objects"_a = py::set{ py::int_(321), py::float_(123.3), "somestring"_s, @@ -277,21 +277,6 @@ TEST_SUBMODULE(pytypes, m) { ); }); - // Some python containers can also be constructed using C++ containers - m.def("init_containers", []() { - return py::dict( - "tuple_array_ints"_a = py::tuple(std::array({1, 2, 3})), - "tuple_deque_floats"_a = py::tuple(std::deque({2.2f, 3.1f, 4.5f})), - "tuple_list_floats"_a = py::tuple(std::list({2.2f, 3.1f, 4.5f})), - "tuple_vector_ints"_a = py::tuple(std::vector({1, 2, 3})), - - "tuple_multiset_int"_a = py::tuple(std::multiset({1, 1, 2, 3})), - // std::set is sorted so insertion order does not matter. Iteration order will be - // one three two - "tuple_set_strings"_a = py::tuple(std::set({"one", "two", "three"})) - ); - }); - 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 a2253b65ac..f6ce629038 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -223,26 +223,17 @@ def test_constructors(): 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] - # This has been created with floats so we don't directly check with doubles - assert tuple(round(x, 1) for x in init_list["tuple_floats"]) == (2.2, 3.1, 4.5, 5.4) - assert [round(x, 1) for x in init_list["list_floats"]] == [2.2, 3.1, 4.5, 5.4] + 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_mixed"] == [ + 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_mixed"] == {321, 123.3, "somestring", (1, 3.3)} - - init_containers = m.init_containers() - assert init_containers["tuple_array_ints"] == (1, 2, 3) - # See above, created with floats - assert tuple((round(x, 1) for x in init_containers["tuple_deque_floats"])) == (2.2, 3.1, 4.5) - assert tuple((round(x, 1) for x in init_containers["tuple_list_floats"])) == (2.2, 3.1, 4.5) - assert init_containers["tuple_vector_ints"] == (1, 2, 3) - assert init_containers["tuple_multiset_int"] == (1, 1, 2, 3) - assert init_containers["tuple_set_strings"] == ("one", "three", "two") + assert init_list["set_objects"] == {321, 123.3, "somestring", (1, 3.3)} def test_pybind11_str_raw_str():