2

I have a struct:

struct some_struct_s {
   int arg1;
   int arg2;
};

I have a C function:

int func(some_struct_s *output);

Both are %included into my SWIG file.

I want some_struct_s *output to be treated like an output parameter. Python example:

int_val, some_struct_output = func()

"Output parameters" is covered in the manual for POD-types (sec 10.1.3), but not for non-POD types.

How do I tell SWIG I want some_struct_s *output to be an output parameter?

1 Answer 1

2

From the documentation:

11.5.7 "argout" typemap

The "argout" typemap is used to return values from arguments. This is most commonly used to write wrappers for C/C++ functions that need to return multiple values. The "argout" typemap is almost always combined with an "in" typemap---possibly to ignore the input value....

Here's a complete example for your code (no error checking for brevity):

%module test

// Declare an input typemap that suppresses requiring any input and
// declare a temporary stack variable to hold the return data.
%typemap(in,numinputs=0) some_struct_s* (some_struct_s tmp) %{
    $1 = &tmp;
%}

// Declare an output argument typemap.  In this case, we'll use
// a tuple to hold the structure data (no error checking).
%typemap(argout) some_struct_s* (PyObject* o) %{
    o = PyTuple_New(2);
    PyTuple_SET_ITEM(o,0,PyLong_FromLong($1->arg1));
    PyTuple_SET_ITEM(o,1,PyLong_FromLong($1->arg2));
    $result = SWIG_Python_AppendOutput($result,o);
%}

// Instead of a header file, we'll just declare this code inline.
// This includes the code in the wrapper, as well as telling SWIG
// to create wrappers in the target language.
%inline %{

struct some_struct_s {
   int arg1;
   int arg2;
};

int func(some_struct_s *output) {
    output->arg1 = 1;
    output->arg2 = 2;
    return 0;
}

%}

Demo below. Note that the int return value of zero as well as the output parameter as a tuple are returned as a list.

>>> import test
>>> test.func()
[0, (1, 2)]

If you don't want typemaps, you can also inject code to create the object and return it to hide it from the user:

%module test

%rename(_func) func; // Give the wrapper a different name

%inline %{

struct some_struct_s {
   int arg1;
   int arg2;
};

int func(struct some_struct_s *output)
{
    output->arg1 = 1;
    output->arg2 = 2;
    return 0;
}

%}

// Declare your interface
%pythoncode %{
def func():
    s = some_struct_s()
    r = _func(s)
    return r, s
%}

Demo:

>>> import test
>>> r, s = test.func()
>>> r
0
>>> s
<test.some_struct_s; proxy of <Swig Object of type 'some_struct_s *' at 0x000001511D70A880> >
>>> s.arg1
1
>>> s.arg2
2

You can make the typemap language agnostic if you carefully select SWIG macros:

%module test

%typemap(in,numinputs=0) struct some_struct_s *output %{
    $1 = malloc(sizeof(struct some_struct_s));
%}

%typemap(argout) struct some_struct_s* output {
    // The last parameter passes ownership of the pointer
    // to Python so it will be freed when the object's
    // reference count goes to zero.
    %append_output(SWIG_NewPointerObj($1, $1_descriptor, SWIG_POINTER_OWN));
}

%inline %{

struct some_struct_s {
   int arg1;
   int arg2;
};

int func(struct some_struct_s *output)
{
    output->arg1 = 1;
    output->arg2 = 2;
    return 0;
}

%}

Demo:

>>> import test
>>> r, s = test.func()
>>> r
0
>>> s
<test.some_struct_s; proxy of <Swig Object of type 'some_struct_s *' at 0x000001DD0425A700> >
>>> s.arg1
1
>>> s.arg2
2
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for this. Without this, I have noticed that I can instantiate an empty some_struct_s on one line of code and pass it in. When the function returns, the contents are non-opaque. This has the benefit of me not having to write a language-specific typemap. I was hoping there was a way to signify the parameter should be at the end without having to write output language-specific api code for each element in an argout typemap. Should I give up hope?
@MichaelLabbé haven't found a way without writing a typemap, but you can avoid language specifics by using some SWIG macros. I'll update with an example.
@MarkTolonen I noticed in the final example, that there is a malloc during typemap(in) without a free using typemap(freearg). Would there be a memory leak as a result?
@Maverickgugu The last parameter of SWIG_newPointerObj() is own. The value 1 passes ownership to Python and it frees the pointer when the Python object's reference count goes to zero. To verify this I built a debug version of the library and passed an invalid pointer to SWIG_newPointerObj(). I called the function but didn't access the returned object. When I exited Python it crashed due to trying to free the invalid pointer value. Changing own to 0 prevented the crash, since the pointer wasn't freed.
@Maverickgugu Updated the example to make the free clear. SWIG had a named constant I could use. Link to docs describing the own parameter: swig.org/Doc4.0/SWIGDocumentation.html#Python_nn64

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.