0

I'm new to swig and I have the following function which i cant fix:

int get_list(IN const char * string, OUT struct entry ** results);

where struct entry is defined:

struct flux_entry
{
    char * addr_str;
    char cc[2];
};

the entry struct is properly converted to a python class. I googled but couldn't find any explanation i could use.
I want to make it return a tuple of: (original get_list int return value, python list of entry python objects, based on the results buffer), but don't know how to convert the C entry to a python object in the argout code snippet. I've managed to get thus far:

%typemap(argout) struct entry **
{
    PyObject *o = PyList_New(0);
    int i;
    for(i=0; $1[i] ; i++)
    {
        PyList_Append(o, SWIG_HOW_TO_CONVERT_TO_PYOBJECT($1[i]));
    }
    $result = o;
}

what should i replace SWIG_HOW_TO_CONVERT_TO_PYOBJECT with? passed results is supposed to be a pointer to a (struct entry *) type, set to NULL before calling get_list and should be set to an allocated array of struct entry * pointers. maybe a small wrapper function could make that easier?

the struct entry array is allocated within the C function using malloc, after calculating (inside get_list) how many elements are needed, and ends with a NULL pointer to indicate the end of the array.

i'd also like to make sure it's freed somewhere :)

thanks!

NirIzr
  • 3,131
  • 2
  • 30
  • 49
  • How do you know how many `struct entry` objects there will be on output? What's the definition of `struct entry`? – Mark Tolonen Jun 23 '13 at 10:41
  • @MarkTolonen: edited the question to provide the extra details – NirIzr Jun 23 '13 at 12:50
  • I actually want to do something very similar to what this guy wanted: http://stackoverflow.com/questions/5822529/swig-returning-an-array-of-doubles?rq=1 but instead of doubles i have an array of struct entry pointers... – NirIzr Jun 23 '13 at 18:43

1 Answers1

1

This should at least give you a starting point that works. I still wasn't sure how the data was returned, since to return an array of pointers so that the final one was NULL I'd think you'd need a struct entry ***, so I just set addr_str = NULL on the last one as a sentinel, and just put some dummy data partially based on the input string into the fields. Modify as needed to suit your needs:

%module example

// Insert the structure definition and function to wrap into the wrapper code.
%{
  struct entry {
    char* addr_str;
    char cc[2];
  };

  int get_list(const char* string, struct entry** results)
  {
    *results = malloc(3 * sizeof(struct entry));
    (*results)[0].addr_str = malloc(10);
    strcpy((*results)[0].addr_str,"hello");
    (*results)[0].cc[0] = string[0];
    (*results)[0].cc[1] = string[1];
    (*results)[1].addr_str = malloc(10);
    strcpy((*results)[1].addr_str,"there");
    (*results)[1].cc[0] = string[2];
    (*results)[1].cc[1] = string[3];
    (*results)[2].addr_str = NULL;
    return 0;
  }
%}

#include <typemaps.i>

// Define the structure for SWIG
struct entry {
  char* addr_str;
  char cc[2];
};

// Define a set of typemaps to be used for an output parameter.

// This typemap suppresses requiring the parameter as an input.
// A temp variable is created and passed instead.
%typemap(in,numinputs=0) struct entry **OUTPUT (struct entry* temp) %{
  $1 = &temp;
%}

// Build a list of tuples containing the two entries from the struct.
// Append the new Python list object to the existing "int" result.
%typemap(argout) struct entry **OUTPUT {
  int i = 0;
  PyObject* out = PyList_New(0);
  while((*$1)[i].addr_str != NULL)
  {
    //PyObject* t = PyTuple_New(2);
    //PyTuple_SET_ITEM(t,0,PyBytes_FromString((*$1)[i].addr_str));
    //PyTuple_SET_ITEM(t,1,PyBytes_FromStringAndSize((*$1)[i].cc,2));
    //PyList_Append(out,t);
    //Py_DECREF(t);
    PyObject* s = SWIG_NewPointerObj(*$1+i,$descriptor(struct entry*),0);
    PyList_Append(out,s);
    Py_DECREF(s);
    ++i;
  }
  $result = SWIG_AppendOutput($result,out);
}

// Since a Python object was created and the data copied for each entry struct,
// free the memory returned in the structure.
//%typemap(freearg) struct entry **OUTPUT {
//  int i=0;
//  while((*$1)[i].addr_str != NULL) {
//    free((*$1)[i].addr_str);
//    ++i;
//  }
//  free(*$1);
//}

// Apply the OUTPUT typemap set to the "results" parameter.
%apply struct entry **OUTPUT {struct entry** results};

// Finally, define the function for SWIG
int get_list(const char* string, struct entry** results);

Demo (Python 3.3):

>>> import example
>>> example.get_list('abcd')
[0, [(b'hello', b'ab'), (b'there', b'cd')]]

Hope that helps.

Edit:

I commented out the tuple creation and just save the entry* proxy instead. This doesn't leak Python objects, but the memory malloced for use by an entry* is not freed. I'm not sure where to put that, although I'm experimenting with %extend.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • Actually, this code will return a Tuple, and not a object for my entry struc, right? another question - why did you `PyDECREF`-ed t? – NirIzr Jun 24 '13 at 00:17
  • Yes, I reflected the data as a tuple, but if you want to leave it as a SWIG proxy object that can be done as well. The decref was because Python objects are reference counted and I was done with the tuple. There was a memory leak without it. – Mark Tolonen Jun 24 '13 at 02:02
  • How can I have a swig proxy? That's my end goal if I wasn't clear in my answer. Isn't the tuple still being used by the list it was appended to? Thanks! – NirIzr Jun 24 '13 at 04:01
  • The reference count gets incremented on the tuple when it is added to the list. I decremented the count when I was done with it, leaving it at a count of 1. When the list is released it will decrement each of its elements. If you build a debug version of Python you can see the sum total of reference counts on all Python objects, so running the command over and over again reveals any leaks. – Mark Tolonen Jun 24 '13 at 05:41
  • @Nirlzr, see my edit above for returning the proxy object instead. – Mark Tolonen Jun 24 '13 at 07:59
  • Awesome! Will this let me access the struct's members from python? – NirIzr Jun 24 '13 at 16:23