Nicholas Cox
2016-06-25 20:37:08 UTC
Hi,
I'm new to this list and signed up because I've been working on
incorporating SWIG into our build system in the robots group at Otherlab.
My background is mostly in control systems and robot software.
Part of what I've been working on is extending the typemaps that come with
SWIG for use in wrapping code for Python to create more general OUTPUT
typemaps (other than the ones for primitive C types defined in
python/typemaps.i. This led me down the road of doing something like this
in an interface file (which I called argout_maps.i):
%define ARGOUT_PTR_MAP(type)
%typemap(in, numinputs=0) type* OUT (type temp) {
$1 = &temp;
}
%typemap(argout) type* OUT {
PyObject* my_out;
my_out = SWIG_NewPointerObj((new $*1_ltype(static_cast< const $*1_ltype&
SWIG_POINTER_OWN | 0);
$result = SWIG_Python_AppendOutput($result, my_out);
}
%enddef
(I suspect that in the above I should be doing some check on (*$1) to make
sure that it is still argout map expects before sending it to
SWIG_NewPointerObj)
I also wanted to combine this with handling std::vector<type> in a similar
way. The std_vector.i file seems to provide a good amount of
functionality, but I couldn't find anything on creating OUTPUT typemaps for
it, so I sketched a quick typemap together to get something like what I
wanted.
%typemap(in, numinputs=0) std::vector<double> *OUT (std::vector<double>
temp) {
$1 = &temp;
}
%typemap(argout) std::vector<double> *OUT {
PyObject* my_out$argnum;
my_out$argnum = swig::from(std::vector<double>(*$1));
$result = SWIG_Python_AppendOutput($result, my_out$argnum);
}
But, I noticed that there is some behavior that seemed somewhat strange to
me. It appears that by default, swig wrapped functions returns lists,
instead of tuples (for which there is a #define SWIG_PYTHON_OUTPUT_TUPLE
flag available to change.) But when this flag is used in combination with
outputting multiple std::vector<> returns, the behavior can be poor. The
returned tuples merge together strangely because std::vectors get mapped to
sequences, which are mapped to tuples. So an output my look like:
(1, 2, 3, 4 (1, 2, 3, 4))
when two lists ((1, 2, 3, 4), (1, 2, 3, 4)) would be the desired return.
Specifically, the SWIG_PYTHON_OUTPUT_TUPLE flag doesn't play well with the
std::vector<> wrapping. One way I could think of to solve this is to allow
an option for SWIG to wrap std::vectors as Python lists instead of Python
tuples. The place that seemed obvious to me to allow this is in
python/pycontainer.swg, by modifying the template to have a similar
compiler #if/#else statement. ie.:
template <class Seq, class T = typename Seq::value_type >
struct traits_from_stdseq {
typedef Seq sequence;
typedef T value_type;
typedef typename Seq::size_type size_type;
typedef typename sequence::const_iterator const_iterator;
static PyObject *from(const sequence& seq) {
%#ifdef SWIG_PYTHON_EXTRA_NATIVE_CONTAINERS
swig_type_info *desc = swig::type_info<sequence>();
if (desc && desc->clientdata) {
return SWIG_NewPointerObj(new sequence(seq), desc, SWIG_POINTER_OWN);
}
%#endif
size_type size = seq.size();
if (size <= (size_type)INT_MAX) {
%#ifdef SWIG_PYTHON_SEQUENCE_AS_LIST
PyObject *obj = PyList_New((Py_ssize_t)size);
Py_ssize_t i = 0;
for (const_iterator it = seq.begin(); it != seq.end(); ++it, ++i) {
PyList_SetItem(obj,i,swig::from<value_type>(*it));
}
%#else
PyObject *obj = PyTuple_New((Py_ssize_t)size);
Py_ssize_t i = 0;
for (const_iterator it = seq.begin(); it != seq.end(); ++it, ++i) {
PyTuple_SetItem(obj,i,swig::from<value_type>(*it));
}
%#endif
return obj;
} else {
PyErr_SetString(PyExc_OverflowError,"sequence size not valid in python");
return NULL;
}
}
};
}
}
Modifying the code like this appears to work and provide the expected
results. Is there a way in which a modification like this to
pycontainer.swg might break some other behavior of SWIG that I am not
seeing? Would this be a useful extension to pycontainer.swg?
I can submit code to show an example of the usage, but I felt that this
email was already code heavy, and I don't know if this is the appropriate
place for that :-)
Cheers,
Nick
I'm new to this list and signed up because I've been working on
incorporating SWIG into our build system in the robots group at Otherlab.
My background is mostly in control systems and robot software.
Part of what I've been working on is extending the typemaps that come with
SWIG for use in wrapping code for Python to create more general OUTPUT
typemaps (other than the ones for primitive C types defined in
python/typemaps.i. This led me down the road of doing something like this
in an interface file (which I called argout_maps.i):
%define ARGOUT_PTR_MAP(type)
%typemap(in, numinputs=0) type* OUT (type temp) {
$1 = &temp;
}
%typemap(argout) type* OUT {
PyObject* my_out;
my_out = SWIG_NewPointerObj((new $*1_ltype(static_cast< const $*1_ltype&
(*$1))),
$1_descriptor,SWIG_POINTER_OWN | 0);
$result = SWIG_Python_AppendOutput($result, my_out);
}
%enddef
(I suspect that in the above I should be doing some check on (*$1) to make
sure that it is still argout map expects before sending it to
SWIG_NewPointerObj)
I also wanted to combine this with handling std::vector<type> in a similar
way. The std_vector.i file seems to provide a good amount of
functionality, but I couldn't find anything on creating OUTPUT typemaps for
it, so I sketched a quick typemap together to get something like what I
wanted.
%typemap(in, numinputs=0) std::vector<double> *OUT (std::vector<double>
temp) {
$1 = &temp;
}
%typemap(argout) std::vector<double> *OUT {
PyObject* my_out$argnum;
my_out$argnum = swig::from(std::vector<double>(*$1));
$result = SWIG_Python_AppendOutput($result, my_out$argnum);
}
But, I noticed that there is some behavior that seemed somewhat strange to
me. It appears that by default, swig wrapped functions returns lists,
instead of tuples (for which there is a #define SWIG_PYTHON_OUTPUT_TUPLE
flag available to change.) But when this flag is used in combination with
outputting multiple std::vector<> returns, the behavior can be poor. The
returned tuples merge together strangely because std::vectors get mapped to
sequences, which are mapped to tuples. So an output my look like:
(1, 2, 3, 4 (1, 2, 3, 4))
when two lists ((1, 2, 3, 4), (1, 2, 3, 4)) would be the desired return.
Specifically, the SWIG_PYTHON_OUTPUT_TUPLE flag doesn't play well with the
std::vector<> wrapping. One way I could think of to solve this is to allow
an option for SWIG to wrap std::vectors as Python lists instead of Python
tuples. The place that seemed obvious to me to allow this is in
python/pycontainer.swg, by modifying the template to have a similar
compiler #if/#else statement. ie.:
template <class Seq, class T = typename Seq::value_type >
struct traits_from_stdseq {
typedef Seq sequence;
typedef T value_type;
typedef typename Seq::size_type size_type;
typedef typename sequence::const_iterator const_iterator;
static PyObject *from(const sequence& seq) {
%#ifdef SWIG_PYTHON_EXTRA_NATIVE_CONTAINERS
swig_type_info *desc = swig::type_info<sequence>();
if (desc && desc->clientdata) {
return SWIG_NewPointerObj(new sequence(seq), desc, SWIG_POINTER_OWN);
}
%#endif
size_type size = seq.size();
if (size <= (size_type)INT_MAX) {
%#ifdef SWIG_PYTHON_SEQUENCE_AS_LIST
PyObject *obj = PyList_New((Py_ssize_t)size);
Py_ssize_t i = 0;
for (const_iterator it = seq.begin(); it != seq.end(); ++it, ++i) {
PyList_SetItem(obj,i,swig::from<value_type>(*it));
}
%#else
PyObject *obj = PyTuple_New((Py_ssize_t)size);
Py_ssize_t i = 0;
for (const_iterator it = seq.begin(); it != seq.end(); ++it, ++i) {
PyTuple_SetItem(obj,i,swig::from<value_type>(*it));
}
%#endif
return obj;
} else {
PyErr_SetString(PyExc_OverflowError,"sequence size not valid in python");
return NULL;
}
}
};
}
}
Modifying the code like this appears to work and provide the expected
results. Is there a way in which a modification like this to
pycontainer.swg might break some other behavior of SWIG that I am not
seeing? Would this be a useful extension to pycontainer.swg?
I can submit code to show an example of the usage, but I felt that this
email was already code heavy, and I don't know if this is the appropriate
place for that :-)
Cheers,
Nick