|
From: Brad H. <fea...@gm...> - 2010-08-29 07:11:56
|
Hi all,
I'm trying to wrap a geometry module I wrote in C for use in Python,
and I want it to be able to use Python sequences of length 2 or 4.
After spending many hours looking at the Swig docs and examples on the
web, I came up with this code for my Swig interface:
%module geometry
%{
// Converts a Python object into a C object of type T.
// There must be a specialization of pyToC<T> in existence in order for the
// conversion to be possible.
// If the Python object could not be converted, an int exception is thrown.
template<typename T> T pyToC(PyObject* o);
// Converts a Python sequence object to a new array of values of type T.
//
// T: The type of the objects in the array, and the type to which objects
// in the Python sequence must be convertible.
//
// o: The Python object to convert.
// a: The array into which the results of the conversion will be stored.
// n: Size of the array.
//
// You can pass any Python object that fully implements the Python
// sequence protocol. However, all objects in the sequence must be of the
// same type, and that type must be one of the types for which there is an
// implementation of pyToC.
//
// An exception of type int is raised when something goes wrong. If an
// exception is raised, no array is allocated and no cleanup is necessary.
// Exception codes:
// 1: Python object is not a sequence.
// 2: Could not determine the size of the sequence.
// 3: Size of the sequence does not match the given size 'n'.
// 4: Could not get one of the items from the sequence.
// 5: One of the objects in the sequence is not convertible to type T.
//
template <typename T>
void
pySequenceToCArray(PyObject *o, T a[], int n) {
if (PySequence_Check(o)) {
int size = PySequence_Length(o);
if (size < 0) throw (2);
if (size != n) throw (3);
for (int i = 0; i < n; i++) {
PyObject* item = PySequence_GetItem(o, i);
if (!item) {
Py_XDECREF(item);
throw (4);
}
try {
a[i] = pyToC<T>(item);
}
catch (int) {
Py_XDECREF(item);
throw (5);
}
Py_DECREF(item);
}
}
else {
throw (1);
}
}
template<> long pyToC<long>(PyObject* o) {
long v = PyInt_AsLong(o);
if (PyErr_Occurred()) throw (1);
return v;
}
template<> int pyToC<int>(PyObject* o) {
int v = (int)PyInt_AsLong(o);
if (PyErr_Occurred()) throw (1);
return v;
}
template<> double pyToC<double>(PyObject* o) {
double v = PyFloat_AsDouble(o);
if (PyErr_Occurred()) throw (1);
return v;
}
template<> float pyToC<float>(PyObject* o) {
float v = (float)PyFloat_AsDouble(o);
if (PyErr_Occurred()) throw (1);
return v;
}
%}
%typemap(in) int [4] {
static int xywh[4];
try {
pySequenceToCArray<int>($input, xywh, 4);
}
catch (int) {
return NULL;
}
$1 = xywh;
}
This compiles fine, and the module imports OK. However, it seems Swig
isn't creating the correct mapping functions, because anywhere I try
to pass a python list to a SWIG-wrapped method, I get a
NotImplementError that lists the available overloads for the given
method, such as the following:
import geometry
r = geometry.IRect([1,2,3,4])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "geometry.py", line 185, in __init__
this = _geometry.new_IRect(*args)
NotImplementedError: Wrong number of arguments for overloaded function
'new_IRect'.
Possible C/C++ prototypes are:
geometry::Rect< int >(int,int,int,int)
geometry::Rect< int >(int const [2],int const [2])
geometry::Rect< int >(geometry::Point< int > const
&,geometry::Point< int > const &)
geometry::Rect< int >(int const [4])
In case it isn't obvious from the output above, IRect is a wrapper
around a C++ geometry::Rect<int> template instantiation, and the
constructor that I'm trying to call is geometry::Rect< int >(int const
[4]). However, it seems like my typemap isn't enough for SWIG to
understand that.
I've been at this for days. What am I doing wrong, or what am I missing?
Thanks in advance,
Brad
|