使一个C ++类看起来像一个numpy数组使用swig [英] Make a C++ class look like a numpy array using swig
问题描述
有什么好的方法暴露一个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);
}
};
限制:
- 该类将提供对原始存储的公开访问(可能通过函数),
- 我不能在C ++头文件/源文件中添加特定于python的代码(我们不想让
对C ++代码有一个Python依赖),因此任何对C ++
端的修改必须通过SWIG完成(例如%extend
)。
方法是在我的SWIG .i
文件
中添加一个 pythoncode
>
%pythoncode {
def arraylike_getitem(self,arg1,arg2):
# b $ b#相当复杂,但涉及到:
#1.为返回值构造一个未初始化的numpy数组
#2.迭代切片指示的索引,
#3.调用self .getValue为每个索引对
#4.返回数组
#将函数添加到ArrayLike类
Arr .__ getitem __ = arraylike_getitem
%}
其中 ArrayLike
数值数据(作为一个平面数组),
并提供成员函数来获取/设置单个值。
主要缺点是步骤1:必须复制我取的我的c数组类的任何切片
。 (主要的优点是,通过返回
numpy数组对象,我知道我可以在任何numpy操作中使用它。)
想象两种改进方法:
- 添加(通过SWIG
%extend
)功能到c类,或者 - 让python函数返回一个数组切片代理对象,
$ b b
我的主要挂机不知道对象需要什么接口(有效地)实现以便像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);
}
};
// ---------------------------------------- -------------------
//文件Arr.cpp
#includeArr.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 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
%模块arr
%{
#includeArr.h
#include< /usr/lib64/python2.7/site-packages/numpy/core/include/numpy /ndarrayobject.h>
#include< python2.7 / Python.h>
%}
%includeArr.h
%pythoncode {
#构造问题):允许
#arr对象和numpy数组之间的操作(例如numpy_array + arr_object是OK)
#但不允许切片(例如numpy_array [:: 2,:: 2] + arr_objec [:: 2,:: 2])
#TODO:找出如何获取没有副本内存的分片
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
,那么我已经成功了。 / p>
编辑:
从这个相关问题它看起来像 __ array_interface __
将是解决方案的一部分)
如果 n_cols
和 n_rows
是(实际上)不可变的,你最好的做法是简单地创建一个实数numpy数组,给它 m_data
code>(n_rows,n_cols)作为shape。这样,您将获得所有numpy数组设施,而不需要任何复制,也不必在您自己的代码中重新实现它们(这将是一个很多的quacking模仿)。
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
并在您的子类中提供此类功能)。
What's a good way to expose a C++ class that provides an array-like interface for use with numpy (scipy)?
By array-like interface I mean something like:
//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);
}
};
Constraints:
- I only care about classes that store their underlying data as contiguous flat arrays,
- The class will provide public access to the raw storage (probably through a function),
- I cannot add python specific code to the C++ header/source files (we do not want
to have a Python dependency for the C++ code) thus any modifications on the C++
side have to be done via SWIG (e.g.
%extend
).
My current approach is to put a pythoncode
block in my SWIG .i
file
that looks something like
%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
%}
where ArrayLike
is the C++ class that holds the numerical data (as a flat array),
and provides member functions to get/set individual values.
The main drawback is step 1. above: I have to make a copy of any slice that I take of my c-array class. (The main advantage is that by returning a numpy array object, I know that I can use it in any numpy operations that I want.)
I can imagine two approaches for improving this:
- Adding (via SWIG
%extend
) additional functionality to the c class, and or - having the python function return an array-slice proxy object,
My main hang-up is not knowing what interface an object needs to (efficiently) implement in order to quack like a numpy array.
Test Case
Here's my test setup:
//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
If I can get this Arr
class to work with numpy
, then I've succeeded.
Edit:
From this related question it looks like __array_interface__
will be part of the solution (TBD: how to use it?)
If n_cols
and n_rows
are (effectively) immutable, your best course of action is to simply create a real numpy array, giving it m_data
as storage and (n_rows, n_cols)
as shape. That way you will get all the numpy array facilities without any copying and without having to reimplement them in your own code (which would be a lot of quacking to imitate).
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);
}
Of course, this won't work as written, since your m_data
member is protected. But it would be a good idea to either make it public or provide an accessor to retrieve it (or inherit from ArrayLike
and provide such functionality in your subclass).
这篇关于使一个C ++类看起来像一个numpy数组使用swig的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!