首页 > 解决方案 > numpy.i 是否向 C++ 发送实际指针,或复制内存?

问题描述

我正在为我工​​作的实验室学习 C++、swig 和 numpy。已指定我必须使用 swig(没有 scypy),我必须使用 numpy 数组,并且我可能不会“将 C 代码带入 python 世界“例如通过在我的界面文件中使用 %import "std_vector" 并让 python 用户创建一个来发送。话虽如此,我正在尝试获得一个 1d numpy 数组(如果他们需要更多维度,我将把它展平) 只通过指针传递到我的 C 代码中 - 我的老板不想花时间复制所有内容,因为效率非常重要。我相信我们使用 c++ 14、python 2.7 和最新版本的 swig,我也在使用 numpy.i。

我将在下面提供我目前正在使用的代码(只是试图在这里获得最小可行)但我很新,虽然它确实有效,但我不确定它实际上是在传递一个指针而不是像我一样复制任何东西想。有人可以确认它是,还是告诉我如何做到这一点?谢谢 x10^99

//The C++ file I am wrapping: 
#ifndef _np_array_to_array_h
#define _np_array_to_array_h

using namespace std;
double getMid(double* myArray, int size){
    int half = size / 2;
    return myArray[half];
}
#endif

///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//\/\

//The interface file: 
%module np_array_to_array

%{
#define SWIG_FILE_WITH_INIT
#include "np_array_to_array.h"
#include <numpy/arrayobject.h>
%}
%include "numpy.i"
%init %{
import_array();
%}
%apply (double* IN_ARRAY1, int DIM1){(double* myArray, int size)};
%include "np_array_to_array.h"

//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/

//How I compile on terminal: 
swig -python -c++ np_array_to_array.i
g++ -fpic -c np_array_to_array_wrap.cxx -I/usr/include/python2.7 
-I/home/sean/Desktop/SerangLab/Swig/numpy/numpy/core/include/ -I/home/sean/.local/lib/python2.7/site-packages/numpy/core/include/numpy/
 g++ -shared np_array_to_array_wrap.o -o _np_array_to_array.so

这将编译并运行,并创建一个成功工作的 python 模块,我使用“from np_array_to_array import *”导入(当在同一目录中时),我可以成功运行 getMid 方法,传入一个 numpyArray 并获得一个双精度输出。如上所述,我根本不确定这是否实际上是通过指针传递(不制作任何副本),因为我没有找到任何可以说明这种方式或另一种方式的东西。有人可以告诉我,如果不是,请解释如何做到这一点?我相信这应该是可能的,因为我认为 numpy 数组使用 c 类型并像 c 一样连续存储内存。

标签: pythonc++numpyswignumpy-ndarray

解决方案


您可以在 SWIG 生成的代码中相当容易地对此进行调查。这有两个部分 - 一个 .py 文件和一个 .cxx 文件。getMid()在其中的每一个中,都会为您的函数生成一些代码。Python 代码只是将所有内容直接传递到 C++ 代码中,在我的系统上最终看起来像这样:

SWIGINTERN PyObject *_wrap_getMid(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
  PyObject *resultobj = 0;
  double *arg1 = (double *) 0 ;
  int arg2 ;
  PyArrayObject *array1 = NULL ;
  int is_new_object1 = 0 ;
  PyObject * obj0 = 0 ;
  double result;

  if (!PyArg_ParseTuple(args,(char *)"O:getMid",&obj0)) SWIG_fail;
  {
    npy_intp size[1] = {
      -1 
    };
    array1 = obj_to_array_contiguous_force_conversion(obj0, NPY_DOUBLE,
      &is_new_object1);
    if (!array1 || !require_dimensions(array1, 1) ||
      !require_size(array1, size, 1)) SWIG_fail;
    arg1 = (double*) array_data(array1);
    arg2 = (int) array_size(array1,0);
  }
  result = (double)getMid(arg1,arg2);
  resultobj = SWIG_From_double(static_cast< double >(result));
  {
    if (is_new_object1 && array1)
    {
      Py_DECREF(array1); 
    }
  }
  return resultobj;
fail:
...

SWIG 和 Python 版本之间不会有太大变化,尽管一些 SWIG 选项会稍微改变它。

从您的问题的角度来看,重要的一点似乎是对obj_to_array_contiguous_force_conversion. 它有一个参数用作输出参数以指示是否分配了新对象。如果最终将其设置为 true,那么在调用之后对象也会被释放。

仅从这一点就可以很安全地得出结论,您的问题的答案是它取决于您传递给函数的输入。如果它已经满足约束(即它是连续的),那么您最终不会制作副本。否则它会,因为你的 C++ 函数需要一个连续的区域。

也应该安全地打赌,如果您使用任何 numpy double 类型,那么您最终将满足此要求并且不会复制,但对于其他数据类型,除非您付出了一些努力,否则不太可能.


推荐阅读