簡體   English   中英

使用swig使C ++類看起來像一個numpy數組

[英]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);
    }
};

約束:

  • 我只關心將底層數據存儲為連續平面數組的類,
  • 該類將提供對原始存儲的公共訪問(可能通過一個函數),
  • 我不能將python特定代碼添加到C ++頭文件/源文件中(我們不希望C ++代碼具有Python依賴性),因此必須通過SWIG(例如%extend )對C ++端進行任何修改。

我目前的方法是在我的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操作中使用它。)

我可以想象兩種改進方法:

  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__將成為解決方案的一部分(TBD:如何使用它?)

如果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並在子類中提供此類功能)將是一個好主意。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM