diff options
author | William S Fulton <wsf@fultondesigns.co.uk> | 2023-04-05 20:21:34 +0100 |
---|---|---|
committer | William S Fulton <wsf@fultondesigns.co.uk> | 2023-04-26 18:18:15 +0100 |
commit | b2fd91bc41050ec4dd8fbb13b71abb796bc6c8b7 (patch) | |
tree | 9c81d693d53c46a44a1afa59fc527df18556f535 | |
parent | 6098b26f3ece2096e14695fd02b040bd0658d2ac (diff) | |
download | swig-b2fd91bc41050ec4dd8fbb13b71abb796bc6c8b7.tar.gz |
Add support for all STL containers to be constructible from a Python set
-rw-r--r-- | CHANGES.current | 7 | ||||
-rw-r--r-- | Examples/test-suite/cpp11_std_array.i | 10 | ||||
-rw-r--r-- | Examples/test-suite/python/cpp11_std_array_runme.py | 26 | ||||
-rw-r--r-- | Examples/test-suite/python/li_std_containers_int_runme.py | 6 | ||||
-rw-r--r-- | Examples/test-suite/python/li_std_set_runme.py | 10 | ||||
-rw-r--r-- | Lib/python/pyclasses.swg | 2 | ||||
-rw-r--r-- | Lib/python/pycontainer.swg | 65 | ||||
-rw-r--r-- | Lib/python/std_array.i | 60 |
8 files changed, 120 insertions, 66 deletions
diff --git a/CHANGES.current b/CHANGES.current index f7c95b320..90cfce74c 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,13 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.2.0 (in progress) =========================== +2023-04-05: wsfulton + [Python] #2515 Add support for all STL containers to be constructible from a Python set. + + The previous implementation used the Python Sequence Protocol to convert from Python types + to STL containers. The new implementation uses the Python Iterator Protocol instead and + thereby can convert from a Python set too. + 2023-03-25: alatina [Octave] #2512 Add support for Octave 8.1. diff --git a/Examples/test-suite/cpp11_std_array.i b/Examples/test-suite/cpp11_std_array.i index 9dc11ce9e..ce87db75a 100644 --- a/Examples/test-suite/cpp11_std_array.i +++ b/Examples/test-suite/cpp11_std_array.i @@ -57,6 +57,16 @@ void arrayInPtr(std::array<int, 6> * myarray) { val *= 10; } } + +std::array<int, 6> overloadFunc(std::array<int, 6> myarray) { + std::array<int, 6> newarray(myarray); + for (auto& val : newarray) { + val *= 100; + } + return newarray; +} +void overloadFunc(int i, int j) { +} %} #endif diff --git a/Examples/test-suite/python/cpp11_std_array_runme.py b/Examples/test-suite/python/cpp11_std_array_runme.py index 9e11a3e5b..dbf9bcee3 100644 --- a/Examples/test-suite/python/cpp11_std_array_runme.py +++ b/Examples/test-suite/python/cpp11_std_array_runme.py @@ -56,6 +56,14 @@ def setslice_exception(swigarray, newval): # print("exception: {}".format(e)) pass +def overload_type_exception(pythonlist): + try: + overloadFunc(pythonlist) + raise RuntimeError("overloadFunc({}) missed raising TypeError exception".format(pythonlist)) + except TypeError as e: +# print("exception: {}".format(e)) + pass + # Check std::array has similar behaviour to a Python list # except it is not resizable @@ -161,3 +169,21 @@ compare_containers(ai, [90, 80, 70, 60, 50, 40]) # fill ai.fill(111) compare_containers(ai, [111, 111, 111, 111, 111, 111]) + +# Overloading +newarray = overloadFunc([9, 8, 7, 6, 5, 4]) +compare_containers(newarray, [900, 800, 700, 600, 500, 400]) + +ai = ArrayInt6([9, 8, 7, 6, 5, 4]) +newarray = overloadFunc([9, 8, 7, 6, 5, 4]) +compare_containers(newarray, [900, 800, 700, 600, 500, 400]) + +overloadFunc(1, 2) +overload_type_exception([1, 2, 3, 4, 5, "6"]) +overload_type_exception([1, 2, 3, 4, 5]) +overload_type_exception([1, 2, 3, 4, 5, 6, 7]) + +# Construct from Python set +myset = {11, 12, 13, 14, 15, 16} +ai = ArrayInt6(myset) +compare_containers(ai, list(myset)) diff --git a/Examples/test-suite/python/li_std_containers_int_runme.py b/Examples/test-suite/python/li_std_containers_int_runme.py index f346de220..13c76d3aa 100644 --- a/Examples/test-suite/python/li_std_containers_int_runme.py +++ b/Examples/test-suite/python/li_std_containers_int_runme.py @@ -279,3 +279,9 @@ try: raise RuntimeError("Zero step not caught") except ValueError: pass + +# Construct from set (Iterator protocol, not Sequence protocol) +ps = {11, 22, 33} +iv = vector_int(ps) +il = vector_int(ps) +compare_containers(list(ps), iv, il) diff --git a/Examples/test-suite/python/li_std_set_runme.py b/Examples/test-suite/python/li_std_set_runme.py index 34a1eb19c..7618f7dc8 100644 --- a/Examples/test-suite/python/li_std_set_runme.py +++ b/Examples/test-suite/python/li_std_set_runme.py @@ -92,3 +92,13 @@ for i in s: if (len(sum) != 3 or (not 1 in sum) or (not "hello" in sum) or (not (1, 2) in sum)): raise RuntimeError + +# Create from Python set +s = set_string({"x", "y", "z"}) +sum = "" +for i in s: + sum = sum + i + +if sum != "xyz": + raise RuntimeError + diff --git a/Lib/python/pyclasses.swg b/Lib/python/pyclasses.swg index 31ebdd2a1..39c4e0316 100644 --- a/Lib/python/pyclasses.swg +++ b/Lib/python/pyclasses.swg @@ -11,7 +11,7 @@ or as a member variable: struct A { - SwigPtr_PyObject obj; + SwigPtr_PyObject _obj; A(PyObject *o) : _obj(o) { } }; diff --git a/Lib/python/pycontainer.swg b/Lib/python/pycontainer.swg index 65223b615..7012b0b1d 100644 --- a/Lib/python/pycontainer.swg +++ b/Lib/python/pycontainer.swg @@ -1015,36 +1015,28 @@ namespace swig { template <class Seq, class T = typename Seq::value_type > struct IteratorProtocol { - static int assign(PyObject *obj, Seq **seq) { - int ret = SWIG_ERROR; - PyObject *iter = PyObject_GetIter(obj); + static void assign(PyObject *obj, Seq *seq) { + SwigVar_PyObject iter = PyObject_GetIter(obj); if (iter) { - PyObject *item = PyIter_Next(iter); - ret = SWIG_OK; - if (seq) - *seq = new Seq(); + SwigVar_PyObject item = PyIter_Next(iter); while (item) { - try { - if (seq) { - (*seq)->insert((*seq)->end(), swig::as<T>(item)); - } else { - if (!swig::check<T>(item)) - ret = SWIG_ERROR; - } - } catch (std::exception& e) { - if (seq) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_TypeError, e.what()); - } - } - ret = SWIG_ERROR; - } - Py_DECREF(item); - item = (ret == SWIG_OK) ? PyIter_Next(iter) : 0; + seq->insert(seq->end(), swig::as<T>(item)); + item = PyIter_Next(iter); } - Py_DECREF(iter); } + } + static bool check(PyObject *obj) { + int ret = false; + SwigVar_PyObject iter = PyObject_GetIter(obj); + if (iter) { + SwigVar_PyObject item = PyIter_Next(iter); + ret = true; + while (item) { + ret = swig::check<T>(item); + item = ret ? PyIter_Next(iter) : 0; + } + } return ret; } }; @@ -1055,11 +1047,9 @@ namespace swig { typedef T value_type; static bool is_iterable(PyObject *obj) { - PyObject *iter = PyObject_GetIter(obj); - bool is_iter = iter != 0; - Py_XDECREF(iter); + SwigVar_PyObject iter = PyObject_GetIter(obj); PyErr_Clear(); - return is_iter; + return iter != 0; } static int asptr(PyObject *obj, sequence **seq) { @@ -1072,7 +1062,22 @@ namespace swig { return SWIG_OLDOBJ; } } else if (is_iterable(obj)) { - ret = IteratorProtocol<Seq, T>::assign(obj, seq); + try { + if (seq) { + *seq = new sequence(); + IteratorProtocol<Seq, T>::assign(obj, *seq); + if (!PyErr_Occurred()) + return SWIG_NEWOBJ; + } else { + return IteratorProtocol<Seq, T>::check(obj) ? SWIG_OK : SWIG_ERROR; + } + } catch (std::exception& e) { + if (seq && !PyErr_Occurred()) + PyErr_SetString(PyExc_TypeError, e.what()); + } + if (seq) + delete *seq; + return SWIG_ERROR; } else if (PySequence_Check(obj)) { try { SwigPySequence_Cont<value_type> swigpyseq(obj); diff --git a/Lib/python/std_array.i b/Lib/python/std_array.i index 9cca563c0..d41d1f15e 100644 --- a/Lib/python/std_array.i +++ b/Lib/python/std_array.i @@ -31,48 +31,38 @@ template <class T, size_t N> struct IteratorProtocol<std::array<T, N>, T> { - static int assign(PyObject *obj, std::array<T, N> **seq) { - int ret = SWIG_ERROR; - PyObject *iter = PyObject_GetIter(obj); + + static void assign(PyObject *obj, std::array<T, N> *seq) { + SwigVar_PyObject iter = PyObject_GetIter(obj); if (iter) { - PyObject *item = PyIter_Next(iter); + SwigVar_PyObject item = PyIter_Next(iter); size_t count = 0; - typename std::array<T, N>::iterator array_iter = nullptr; - ret = SWIG_OK; - if (seq) { - *seq = new std::array<T, N>(); - array_iter = (*seq)->begin(); - } + typename std::array<T, N>::iterator array_iter = seq->begin(); while (item && (count < N)) { - try { - if (seq) { - *array_iter++ = swig::as<T>(item); - } else { - if (!swig::check<T>(item)) - ret = SWIG_ERROR; - } - } catch (std::exception& e) { - if (seq) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_TypeError, e.what()); - } - } - ret = SWIG_ERROR; - } ++count; - Py_DECREF(item); - item = (ret == SWIG_OK) ? PyIter_Next(iter) : 0; - } - if ((ret == SWIG_OK) && (count != N || item)) { - PyErr_SetString(PyExc_TypeError, "std::array size does not match source container size"); - ret = SWIG_ERROR; + *array_iter++ = swig::as<T>(item); + item = PyIter_Next(iter); } - Py_XDECREF(item); - Py_DECREF(iter); - if (seq && (ret == SWIG_ERROR)) - delete *seq; + if (count != N || item) + throw std::invalid_argument("std::array size does not match source container size"); } + } + static bool check(PyObject *obj) { + int ret = false; + SwigVar_PyObject iter = PyObject_GetIter(obj); + if (iter) { + SwigVar_PyObject item = PyIter_Next(iter); + size_t count = 0; + ret = true; + while (item && (count < N)) { + ++count; + ret = swig::check<T>(item); + item = ret ? PyIter_Next(iter) : 0; + } + if (count != N || item) + ret = false; + } return ret; } }; |