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

通过类似数组的接口(interface),我的意思是这样的:

//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);
    }
};

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

  • 我目前的方法是在我的 SWIG pythoncode 文件中放置一个 .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 操作中使用它。)

    我可以想象两种改进方法:
  • 向 c 类或
  • 添加(通过 SWIG %extend)附加功能
    具有 python 函数的
  • 返回一个数组切片代理对象,

  • 我的主要问题是不知道对象需要(有效地)实现什么接口(interface)才能像 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__ 将成为解决方案的一部分(待定:如何使用它?)

    最佳答案

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

    关于c++ - 使用 swig 使 C++ 类看起来像一个 numpy 数组,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18211783/

    10-14 12:49
    查看更多