什么是公开 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
)完成。
我目前的方法是pythoncode
在我的 SWIG.i
文件中放置一个类似于
%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 操作中使用它。)
我可以想象两种改进方法:
%extend
向c 类添加(通过 SWIG )附加功能,和或- 让 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__
将成为解决方案的一部分(待定:如何使用它?)