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

Make a C++ class look like a numpy array using swig

本文关键字:一个 数组 numpy swig C++ 看起来 使用      更新时间:2023-10-16

公开一个提供类似数组的接口以用于 numpy (scipy( 的 C++ 类的好方法是什么?

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

//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 数组类中获取。 (主要优点是,通过返回numpy 数组对象,我知道我可以在我想要的任何 numpy 操作中使用它。

我可以想象两种方法来改善这一点:

  1. 添加(通过SWIG %extend(c类的附加功能,并且或
  2. 让 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一起工作,那么我就成功了。

编辑:从这个相关问题来看,__array_interface__将成为解决方案的一部分(待定:如何使用它?

如果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继承并在子类中提供此类功能(。

相关文章: