GenericAlias GenericAlias - 3 months ago 26
C Question

SWIG: How to return a struct using %apply? 'No typemaps defined' warning

I currently have a function that uses a struct as a buffer to return some information, like so:

int example_reader(int code, void* return_struct);


My goal is to make it so that when I wrap this function using SWIG so that it can be used in Python, I will return the struct along with the function's regular return value. Thus far, I have been doing so using the %apply command like so:

%apply struct ret_struct *OUTPUT {void* return_struct};


However, when I add the above line to my .i file and try to run SWIG, I get the following warning:

"warning 453: Can't apply (struct ret_struct *OUTPUT. No typemaps are defined"

I believe I'm including the .h file that defines the struct I'm trying to return, so I've had trouble pinpointing the issue. Please correct me if the issue seems to involve the improper inclusion of the struct. I've tried reading through the SWIG documentation as well as other Stack Overflow posts to get some inkling of what the problem might be, but I haven't been able to figure it out thus far. The problem is made slightly trickier because I am trying to return a void pointer to a struct, and the code I'm trying to wrap could have multiple kinds of structs for me to return. What would be a wise way of handling the return of this struct? Thank you!

Answer

I have given here a full C example, where an interface is used for returning a struct to the target language together with a return value. In this way you can make a proper interface, where no implementation is given in the header. That is no default implementation of a virtual destructor. If you don't want to use an interface, you can let SWIG and Python know how data are represented.

Interface header: foo.h

typedef struct _Foo Foo;

int foo_new(Foo **obj);
int foo_free(Foo *obj);

int foo_get_value_a(Foo *obj, int *result);
int foo_set_value_a(Foo *obj, int value);

int foo_get_value_b(Foo *obj, char **result);
int foo_set_value_b(Foo *obj, char *value);

SWIG interface: foo.i

%module foo
%{
#include "foo.h"
%}

%include "typemaps.i"

%typemap(in, numinputs=0) Foo ** (Foo *temp) {
  $1 = &temp;
}

%typemap(argout) Foo ** {
  PyObject* temp = NULL;
  if (!PyList_Check($result)) {
    temp = $result;
    $result = PyList_New(1);
    PyList_SetItem($result, 0, temp);
  }
  temp = SWIG_NewPointerObj(*$1, SWIGTYPE_p__Foo, SWIG_POINTER_NEW);
  PyList_Append($result, temp);
  Py_DECREF(temp);
}

%delobject foo_free; // Protect for double deletion

struct _Foo {};
%extend _Foo {
  ~_Foo() {
    foo_free($self);
  }
};
%ignore _Foo;

Some implementation of the interface: foo.c

%include "foo.h"

#include "foo.h"
#include "stdlib.h"
#include "string.h"

struct FooImpl {
  char* c;
  int i;
};

int foo_new(Foo **obj)
{
  struct FooImpl* f = (struct FooImpl*) malloc(sizeof(struct FooImpl));
  f->c = NULL;
  *obj = (Foo*) f;
  return 0;
}
int foo_free(Foo *obj)
{
  struct FooImpl* impl = (struct FooImpl*) obj;
  if (impl) {
    if (impl->c) {
      free(impl->c);
      impl->c = NULL;
    }
  }
  return 0;
}

int foo_get_value_a(Foo *obj, int *result)
{
  struct FooImpl* impl = (struct FooImpl*) obj;
  *result = impl->i;
  return 0;
}
int foo_set_value_a(Foo *obj, int value)
{
  struct FooImpl* impl = (struct FooImpl*) obj;
  impl->i = value;
  return 0;
}

int foo_get_value_b(Foo *obj, char **result)
{
  struct FooImpl* impl = (struct FooImpl*) obj;
  *result = impl->c;
  return 0;
}
int foo_set_value_b(Foo *obj, char *value)
{
  struct FooImpl* impl = (struct FooImpl*) obj;
  int len = strlen(value);
  if (impl->c) {
    free(impl->c);
  }
  impl->c = (char*)malloc(len+1);
  strcpy(impl->c,value);
  return 0;
}

Script for building

#!/usr/bin/env python
from distutils.core import setup, Extension
import os

os.environ['CC'] = 'gcc';
setup(name='foo',
      version='1.0',
      ext_modules =[Extension('_foo',
                              ['foo.i','foo.c'])])

Usage:

import foo

OK, f = foo.foo_new()
OK = foo.foo_set_value_b(f, 'Hello world!')
OK = foo.foo_free(f)

OK, f = foo.foo_new()
# Test safe to double delete
del f
Comments