numpy.i 是不是向 C++ 发送实际指针,或复制内存?

Posted

技术标签:

【中文标题】numpy.i 是不是向 C++ 发送实际指针,或复制内存?【英文标题】:Does numpy.i send actual pointers to C++, or make copies of memory?numpy.i 是否向 C++ 发送实际指针,或复制内存? 【发布时间】:2019-06-19 20:22:48 【问题描述】:

我正在为我工​​作的实验室学习 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 并获得一个 double退出。如上所述,我只是不确定这是否实际上是通过指针传递(不制作任何副本),因为我没有找到任何可以说明这种方式的东西。有人可以告诉我,如果不是,请解释如何做到这一点?我相信这应该是可能的,因为我认为 numpy 数组使用 c 类型并像 c 一样连续存储内存。

【问题讨论】:

【参考方案1】:

您可以在 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 双精度类型,那么您最终将满足此要求并且不会进行复制,但对于其他数据类型,除非您去过有点努力。

【讨论】:

嘿,谢谢伙计,回答得很好!我认为我确实在某个时候检查过,但我太新了,如果不是我自己的代码,我并不总是(AKA 通常不知道)知道我在看什么。谢谢你的解释!

以上是关于numpy.i 是不是向 C++ 发送实际指针,或复制内存?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 读取 LPCVOID 指针的值

找不到 numpy.i

如何使用 pinvoke 将图像从 c 或 c++ 发送到 C#

不评估应用了 sizeof 的表达式是不是使得在 C++ 中取消引用 sizeof 内的空指针或无效指针是合法的?

如何在封装在类中的回调函数上发送指针

C++ 风格的转换运算符是不是会更改它转换的指针?