[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