Add support for all STL containers to be constructible from a Python set

This commit is contained in:
William S Fulton 2023-04-05 20:21:34 +01:00
parent 6098b26f3e
commit b2fd91bc41
8 changed files with 126 additions and 72 deletions

View File

@ -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.

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -11,7 +11,7 @@
or as a member variable:
struct A {
SwigPtr_PyObject obj;
SwigPtr_PyObject _obj;
A(PyObject *o) : _obj(o) {
}
};

View File

@ -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);

View File

@ -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);
if (iter) {
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();
}
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;
}
Py_XDECREF(item);
Py_DECREF(iter);
if (seq && (ret == SWIG_ERROR))
delete *seq;
}
static void assign(PyObject *obj, std::array<T, N> *seq) {
SwigVar_PyObject iter = PyObject_GetIter(obj);
if (iter) {
SwigVar_PyObject item = PyIter_Next(iter);
size_t count = 0;
typename std::array<T, N>::iterator array_iter = seq->begin();
while (item && (count < N)) {
++count;
*array_iter++ = swig::as<T>(item);
item = PyIter_Next(iter);
}
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;
}
};