Is there any way to use pythonappend with SWIG’s new builtin feature?

I agree with you that using typemap gets a little messy, but it is the right way to accomplish this task. You are also right that the SWIG documentation does not directly say that %pythonappend is incompatible with -builtin, but it is strongly implied: %pythonappend adds to the Python proxy class, and the Python proxy class does not exist at all in conjunction with the -builtin flag.

Before, what you were doing was having SWIG convert the C++ std::vector objects into Python tuples, and then passing those tuples back down to numpy – where they were converted again.

What you really want to do is convert them once, at the C level.

Here’s some code which will turn all std::vector<int> objects into NumPy integer arrays:

%{
#include "numpy/arrayobject.h"
%}

%init %{
    import_array();
%}

%typemap(out) std::vector<int> {
    npy_intp result_size = $1.size();

    npy_intp dims[1] = { result_size };

    PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT);
    int* dat = (int*) PyArray_DATA(npy_arr);

    for (size_t i = 0; i < result_size; ++i) {
        dat[i] = $1[i];
    }

    $result = PyArray_Return(npy_arr);
}

This uses the C-level numpy functions to construct and return an array. In order, it:

  • Ensures NumPy’s arrayobject.h file is included in the C++ output file
  • Causes import_array to be called when the Python module is loaded (otherwise, all NumPy methods will segfault)
  • Maps any returns of std::vector<int> into NumPy arrays with a typemap

This code should be placed before you %import the headers which contain the functions returning std::vector<int>. Other than that restriction, it’s entirely self-contained, so it shouldn’t add too much subjective “mess” to your codebase.

If you need other vector types, you can just change the NPY_INT and all the int* and int bits, otherwise duplicating the function above.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)