Search code examples
pythonc++structswigstdvector

Using SWIG for std::vector members in C++ structs


I am trying to use SWIG to wrap a C++ struct containing vector members, and provide an interface with Python. I have a small test case using the following three files (Ubuntu-16.04):

A Makefile that looks like this:

FLAGS = -fPIC

PYTHONI = -I /usr/include/python3.5/
PYTHONL = -Xlinker -export-dynamic

all:
    swig -Wall -c++ -python -o test.cxx test.i
    g++ $(FLAGS) $(PYTHONI) -c test.cxx -o test_wrap.o
    g++ $(PYTHONL) $(LIBFLAGS) -shared -o _test.so test_wrap.o -lstdc++

A test C++ header defining a struct here (test.h):

#ifndef TEST_H
#define    TEST_H

#include <vector>

using std :: vector;

   struct myStruct {
      
      float var1; // m
      vector<float> arr1; // m
      vector<float> arr2; // C
      int var2;
   };

#endif

And a SWIG interface file here (test.i):

%module test
%include "std_vector.i"

typedef std::vector<float> FloatVector;

namespace std {
   %template(FloatVector) vector<float>;
};

%naturalvar myStruct::arr1;

%{
#include <vector>
#include "test.h"
%}

%include "test.h"

Running make all creates a test.py file, which can be imported and creates the myStruct class:

import test
foo = test.myStruct()

I can set the var1 and var2 members with calls like so:

foo.var1 = 1.23
foo.var2 = 4

However, I cannot make edits to the arr1 or arr2 members. These are both of type 'SwigPyObject', even though I have tried to use the %naturalvar on arr1. What I want to do is create a structure, populate it with data from my Python session, and then use that structure in other calls to C++ functions.


Solution

  • Using using in a header file is a bad idea. SwigPyObject type indicates type typemaps weren't matched properly. For example, this works:

    test.i:

    %module test
    
    %include <std_vector.i>
    %template(FloatVector) std::vector<float>;
    %naturalvar myStruct::arr1;
    %naturalvar myStruct::arr2;
    
    %inline %{
    #include <vector>
    struct myStruct {
        float var1;
        std::vector<float> arr1;
        std::vector<float> arr2;
        int var2;
    };
    %}
    

    Demo:

    >>> import test
    >>> foo = test.myStruct()
    >>> foo.arr1
    ()
    >>> foo.var1=1.23
    >>> foo.var2=5
    >>> foo.arr1=1,2,3
    >>> foo.arr2=1.1,2.2
    >>> foo
    <test.myStruct; proxy of <Swig Object of type 'myStruct *' at 0x000001F94E7487E0> >
    >>> foo.var1
    1.2300000190734863
    >>> foo.var2
    5
    >>> foo.arr1
    (1.0, 2.0, 3.0)
    >>> foo.arr2
    (1.100000023841858, 2.200000047683716)
    

    Without %naturalvar the values can still be assigned, but need:

    >>> foo.arr1 = test.FloatVector([1,2,3])