[Numpy-discussion] SWIG wrappers: Passing NULL pointers or arrays
Albert Strasheim
fullung at gmail.com
Tue Apr 25 23:42:05 EDT 2006
Hello all,
I've currently wrapping a C library (libsvm) with NumPy. libsvm has a few
structs similiar to the following:
struct svm_parameter {
double* weight;
int nr_weight;
};
In my SWIG wrapper I did the following:
struct svm_parameter {
%immutable;
int nr_weight;
%mutable;
double* weight;
%extend {
svm_parameter() {
struct svm_parameter* param = (struct svm_parameter*)
malloc(sizeof(struct svm_parameter));
param->nr_weight = 0; param->weight = 0;
return param;
}
~svm_parameter() {
free(self->weight); free(self);
}
void _set_weight(double* IN_ARRAY1, int DIM1) {
free(self->weight);
self->nr_weight = DIM1;
self->weight = malloc(sizeof(double) * DIM1);
if (!self->weight) {
SWIG_exception(SWIG_MemoryError, "OOM");
}
memcpy(self->weight, IN_ARRAY1, sizeof(double) * DIM1);
return;
fail:
self->nr_weight = 0;
self->weight = 0;
}
}
};
This works pretty well (suggestion welcome though). However, one feature
that I think is lacking from the current array typemaps is a way of passing
NULL to the C function. On the Python side I want to be able to do:
svm_parameter.weight = N.array([1.0,2.0])
or
svm_parameter.weight = None
This heads off to __setattr__ where the following happens:
def __setattr__(self, attr, val):
if attr in ['weight', 'weight_label']:
set_func = getattr(self, '_set_%s' % (attr,))
set_func(val)
else:
super(svm_parameter, self).__setattr__(attr, val)
At this point the typemap magic kicks in. However, passing a None doesn't
work, because somewhere down the line somebody checks for the int argument.
The current typemap looks like this:
%define TYPEMAP_IN1(type,typecode)
%typemap(in) (type* IN_ARRAY1, int DIM1)
(PyArrayObject* array=NULL, int is_new_object) {
int size[1] = {-1};
array = obj_to_array_contiguous_allow_conversion($input, typecode,
&is_new_object);
if (!array || !require_dimensions(array,1) || !require_size(array,size,1))
SWIG_fail;
$1 = (type*) array->data;
$2 = array->dimensions[0];
}
%typemap(freearg) (type* IN_ARRAY1, int DIM1) {
if (is_new_object$argnum && array$argnum) Py_DECREF(array$argnum);
}
%enddef
I quickly hacked up the following typemap that seems to deal gracefully when
a None is passed instead of an array. Changed lines:
if ($input == Py_None) {
is_new_object = 0;
$1 = NULL;
$2 = 0;
} else {
int size[1] = {-1};
array = obj_to_array_contiguous_allow_conversion($input, typecode,
&is_new_object);
if (!array || !require_dimensions(array,1) || !require_size(array,size,1))
SWIG_fail;
$1 = (type*) array->data;
$2 = array->dimensions[0];
}
Now I can write my set_weight function as follows:
void _set_weight(double* IN_ARRAY1, int DIM1) {
free(self->weight);
self->weight = 0;
self->nr_weight = DIM1;
if (DIM1 > 0) {
self->weight = malloc(sizeof(double) * DIM1);
if (!self->weight) {
SWIG_exception(SWIG_MemoryError, "OOM");
}
memcpy(self->weight, IN_ARRAY1, sizeof(double) * DIM1);
}
return;
fail:
self->nr_weight = 0;
}
Does it make sense to add this to the typemaps? Any other comments? Are
there better ways to accomplish this?
Regards,
Albert
More information about the NumPy-Discussion
mailing list