summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam S Fulton <wsf@fultondesigns.co.uk>2023-04-05 20:21:34 +0100
committerWilliam S Fulton <wsf@fultondesigns.co.uk>2023-04-26 18:18:15 +0100
commitb2fd91bc41050ec4dd8fbb13b71abb796bc6c8b7 (patch)
tree9c81d693d53c46a44a1afa59fc527df18556f535
parent6098b26f3ece2096e14695fd02b040bd0658d2ac (diff)
downloadswig-b2fd91bc41050ec4dd8fbb13b71abb796bc6c8b7.tar.gz
Add support for all STL containers to be constructible from a Python set
-rw-r--r--CHANGES.current7
-rw-r--r--Examples/test-suite/cpp11_std_array.i10
-rw-r--r--Examples/test-suite/python/cpp11_std_array_runme.py26
-rw-r--r--Examples/test-suite/python/li_std_containers_int_runme.py6
-rw-r--r--Examples/test-suite/python/li_std_set_runme.py10
-rw-r--r--Lib/python/pyclasses.swg2
-rw-r--r--Lib/python/pycontainer.swg65
-rw-r--r--Lib/python/std_array.i60
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;
}
};