I am using SWIG to create python wrappers for a C++ library. The library has a few public std::vector member variables that I would like to use as lists in python. However, I haven't been able to found a solution that works. Below is a simplified example which illustrates my current solution:
example.h
#pragma once
#include <vector>
#if defined _WIN32
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
namespace Example {
class EXPORT MyClass {
public:
std::vector<int> my_data{1, 2, 3};
std::vector<int> get_vec();
};
}
example.cpp
#include "example.h"
namespace Example {
std::vector<int> MyClass::get_vec() {
std::vector<int> v{1, 3, 5};
return v;
}
}
example.i
%module example
%include "stdint.i"
%include "std_vector.i"
%naturalvar;
%{
#include <example.h>
%}
%template(int_vector) std::vector<int>;
%include <example.h>
I'am also attaching the CMakeLists.txt file I use, in case anyone wants to build the project.
cmake_minimum_required(VERSION 3.15)
project(CMakeSwigExample LANGUAGES CXX)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_library(example_cpp SHARED example.h example.cpp)
target_compile_features(example_cpp PUBLIC cxx_std_17)
if (UNIX)
set_target_properties(example_cpp PROPERTIES INSTALL_RPATH "$ORIGIN")
endif()
FIND_PACKAGE(SWIG REQUIRED)
INCLUDE(${SWIG_USE_FILE})
FIND_PACKAGE(PythonLibs)
SET(CMAKE_SWIG_FLAGS "")
SET_SOURCE_FILES_PROPERTIES(example.i PROPERTIES CPLUSPLUS ON)
set_property(SOURCE itnfileio.i PROPERTY SWIG_MODULE_NAME example)
SWIG_ADD_LIBRARY(example
TYPE SHARED
LANGUAGE python
SOURCES example.i)
target_include_directories(example
PRIVATE
${PYTHON_INCLUDE_DIRS})
target_link_libraries(example PRIVATE example_cpp ${PYTHON_LIBRARIES})
Here is an example of how the generated wrapper can be used in python
>>> from example import MyClass
>>> m = MyClass()
>>> m.get_vec()
(1, 3, 5)
>>> m.my_data
(1, 2, 3)
>>> m.my_data = [9, 8, 7]
>>> m.my_data.append(6)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'append'
This is close to what I want, but since std_vector.i converts std::vector to a tuple, it is not possible to append elements to the my_data variable. If I remove the %naturalvar from example.i I can use list methods like append on my_data. However, I then get an error message if I try to assign the variable to a python list (since the type of my_data then is a proxy of a swig object).
I have tried adding a typemap to example.i file. Then the get_vec method returned a python list, but the type of my_data did not change. This was the typemap tried to add to example.i
%typemap(out) std::vector<int> (PyObject* tmp) %{
tmp = PyList_New($1.size());
for(int i = 0; i < $1.size(); ++i)
PyList_SET_ITEM(tmp,i,PyLong_FromLong($1[i]));
$result = SWIG_Python_AppendOutput($result,tmp);
%}
What do I need to do to be able to use my_data as a normal list in python? Is typemaps the way to go, and if so, what would it look like?