Search code examples
pythonarraysglobal-variablesswigextend

extending global c variable array with swig and python


I have a global variable array in c that I'd like to pull into python. And I'm having difficulties with varout typemap:

/* example.c */
int foo[] = {0, 1};

And here is the very vanilla interface:

/* example.i */
%module example
%{     
 extern int foo[2];
%}

%typemap(varout) int foo[] {
  int i;
  //$1, $1_dim0, $1_dim1
  $result = PyList_New($1_dim0);
  for (i = 0; i < $1_dim0; i++) {
    PyObject *o = PyInt_FromLong((double) $1[i]);
    PyList_SetItem($result,i,o);
  }
}

%include "example.c"

When I try to build it with the following SConstruct:

import distutils.sysconfig
env = Environment(SWIGFLAGS='-python -shadow -Wall'.split(),
                  CPPPATH=[distutils.sysconfig.get_python_inc()],
                  SHLIBPREFIX="")
env.SharedLibrary('_example.so', ['example.c', 'example.i'])

$1_dim0 special variable is not populated, resulting in the following non-compilable code in example_wrap.c:

SWIGINTERN PyObject *Swig_var_foo_get(void) {
  PyObject *pyobj = 0;

  {
    int i;
    //foo, , foo_dim1
    pyobj = PyList_New();
    for (i = 0; i < ; i++) {
      PyObject *o = PyInt_FromLong((double) foo[i]);
      PyList_SetItem(pyobj,i,o);
    }
  }
  return pyobj;
}

So clearly the typemap match has happened, but dimensionality of array is missing. What am I missing? Hard coding the dimension does works.

In general, is there any way to extend global cvar variables with swig?

$ swig -version
SWIG Version 2.0.4
Compiled with g++ [i686-pc-linux-gnu]
Configured options: +pcre
Please see http://www.swig.org for reporting bugs and further information

Solution

  • You're almost there with your varout typemap. You need to make two minor changes:

    1. You need to add the size ANY to the int foo[] typemap:

      %typemap(varout) int foo[ANY] {
        int i;
        //$1, $1_dim0, $1_dim1
        $result = PyList_New($1_dim0);
        for (i = 0; i < $1_dim0; i++) {
          PyObject *o = PyInt_FromLong((double) $1[i]);
          PyList_SetItem($result,i,o);
        }
      }
      

      This makes sure your typemap is a match for arrays of (any) known size, not just equivalent to int *foo.

    2. You need to modify example.c to make the size of foo clearer. It's legal and correct C as it stands but tricky to deduce the size of the array unless you happen to be a complete C compiler. Changing it to:

      int foo[2] = {0, 1};
      

      is sufficient to make sure that it matches the varout typemap.

    With those two changes the generated code works as you'd hope:

    SWIGINTERN PyObject *Swig_var_foo_get(void) {
      PyObject *pyobj = 0;
    
      {
        int i;
        //foo, 2, foo_dim1
        pyobj = PyList_New(2);
        for (i = 0; i < 2; i++) {
          PyObject *o = PyInt_FromLong((double) foo[i]);
          PyList_SetItem(pyobj,i,o);
        }
      }
      return pyobj;
    }
    

    is what gets generated on my machine with those changes.