使用 swig 使 C++ 类看起来像一个 numpy 数组

Posted

技术标签:

【中文标题】使用 swig 使 C++ 类看起来像一个 numpy 数组【英文标题】:Make a C++ class look like a numpy array using swig 【发布时间】:2013-08-13 14:25:55 【问题描述】:

什么是公开 C++ 类的好方法,该类提供类似数组的接口以与 numpy (scipy) 一起使用?

通过类似数组的接口,我的意思是:

//file:Arr.h
class Arr
public:
    int n_rows;
    int n_cols;
    float* m_data;

    Arr(int r, int c, float v);
    virtual ~Arr();
    float get(int i, int j);
    void set(int i, int j, float v);

    long data_addr()
        return (long)(m_data);
    
;

约束:

我只关心将其基础数据存储为连续平面数组的类, 该类将提供对原始存储的公共访问(可能通过函数), 我无法将 python 特定代码添加到 C++ 头文件/源文件中(我们不希望 对 C++ 代码具有 Python 依赖项)因此对 C++ 的任何修改 必须通过 SWIG 完成(例如 %extend)。

我目前的方法是在我的 SWIG .i 文件中放置一个 pythoncode 块 看起来像

%pythoncode
def arraylike_getitem(self, arg1,arg2 ):
   # the actual implementation to handle slices
   # is pretty complicated but involves:
   # 1. constructing an uninitialized  numpy array for return value
   # 2. iterating over the indices indicated by the slices,
   # 3. calling self.getValue for each of the index pairs,
   # 4. returning the array

# add the function to the ArrayLike class
Arr.__getitem__=arraylike_getitem
%

其中ArrayLike 是保存数字数据(作为平面数组)的 C++ 类, 并提供成员函数来获取/设置单个值。

主要缺点是上面的第 1 步:我必须复制任何切片 我参加了我的 c-array 课程。 (主要优点是通过返回一个 numpy 数组对象,我知道我可以在任何我想要的 numpy 操作中使用它。)

我可以想象两种改进方法:

    向 c 类添加(通过 SWIG %extend)附加功能,和或 让 python 函数返回一个数组切片代理对象,

我的主要问题是不知道对象需要(有效)实现什么接口才能像 numpy 数组一样嘎嘎作响。

测试用例

这是我的测试设置:

//file:Arr.h
class Arr
public:
    int n_rows;
    int n_cols;
    float* m_data;

    Arr(int r, int c, float v);
    virtual ~Arr();
    float get(int i, int j);
    void set(int i, int j, float v);

    long data_addr()
        return (long)(m_data);
    
;

//-----------------------------------------------------------

//file Arr.cpp
#include "Arr.h"

Arr::Arr(int r, int c, float v): n_rows(r), n_cols(c), m_data(0)
    m_data=new float[ r*c ];
    for( int i=0; i<r*c; ++i)
        m_data[i]=v;
    
  
Arr::~Arr()
    delete[] m_data;


float Arr::get(int i, int j)
    return m_data[ i*n_cols+j];

void Arr::set(int i, int j, float v)
    m_data[i*n_cols+j]=v;


//--------------------------------------------------------------------
//file:arr.i
%module arr

%
#include "Arr.h"
#include </usr/lib64/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h>
#include <python2.7/Python.h>
%

%include "Arr.h"


%pythoncode

# Partial solution (developed in constructing the question): allows operations between 
# arr objects and numpy arrays (e.g. numpy_array+arr_object is OK)
# but does not allow slicing (e.g. numpy_array[::2,::2]+arr_objec[::2,::2])
# TODO: figure out how to get slices without copy memory
def arr_interface_map(self):
    res= 'shape':(self.n_rows, self.n_cols), 'typestr':'<f4', 'data': self.data_addr(),0), 'version':3 
    return res

Arr.__array_interface__=property( arr_interface_map )




//---------------------------------------------------------
#file: Makefile
INCLUDE_FLAGS = -I/usr/include/python2.7 

arr_wrap.cpp: arr.i Arr.h
     swig -c++ -python -o $@ $INCLUDE_FLAGS arr.i

_arr.so: arr_wrap.o Arr.o
    g++ -shared -o _arr.so arr_wrap.o Arr.o 

clean:
    rm -f *.o *_wrap.cpp *.so

all: _arr.so

如果我可以让这个Arr 类与numpy 一起工作,那么我就成功了。

编辑: 从this related question 看来__array_interface__ 将成为解决方案的一部分(待定:如何使用它?)

【问题讨论】:

【参考方案1】:

如果n_colsn_rows (实际上)是不可变的,那么最好的做法是简单地创建一个真正的numpy 数组,将m_data 作为存储,(n_rows, n_cols) 作为形状。这样,您将获得所有 numpy 数组工具,而无需任何复制,也无需在您自己的代码中重新实现它们(这将是一个 很多 嘎嘎模仿)。

PyObject* array_like_to_numpy(ArrayLike& obj)

    npy_intp dims[] =  obj.n_rows, obj.n_cols ;
    return PyArray_SimpleNewFromData(2, dims, NPY_FLOAT, obj.m_data);

当然,这不会像所写的那样工作,因为您的m_data 成员受到保护。但最好将其公开或提供访问器来检索它(或从ArrayLike 继承并在您的子类中提供此类功能)。

【讨论】:

对我不起作用:调用 array_like_to_numpy 会导致分段错误。 @Dave 你能提供一个最小的可编译的段错误示例吗?没有它,很难猜测可能出了什么问题。 我将您的方法视为“将上面的函数放入 % ...% 文字包含块中的 .i 文件中,然后将该函数包装在 swig 文件的接口定义部分中。 @Dave 我不熟悉 SWIG 的工作原理,所以我不知道这是否正确。然而,根据documentation,我提供的简单函数应该ArrayLike 转换为Python 对象。 事实上,您可以通过在程序的某处添加诸如PyObject_SetAttrString(PyImportImportModule("sys"), "my_array", array_like_to_numpy(*new ArrayLike())) 之类的行来测试该函数是否在没有 SWIG 的情况下工作。如果sys.my_array 函数作为一个numpy 数组,则该函数正常工作。

以上是关于使用 swig 使 C++ 类看起来像一个 numpy 数组的主要内容,如果未能解决你的问题,请参考以下文章

Swig:返回向量的接口简单 C++ 类

使用来自 c# 的 c++ uint8_t * 使用 SWIG

Swig C++ to C#:如何从 C++ 包装类以使模板类中的方法在 C# 的派生类中可用?

SWIG:你能否使用 SWIG 专门使用 C++ 头文件使 C++ 在 Python 中可用?

在 Swig 中通过 ref 返回动态数组

使用 SWIG 包装调用另一个对象成员函数的 C++ 类