C++类成员函数返回PyObject*分段错误

C++ class member function returns PyObject* segmentation fault

本文关键字:分段 错误 PyObject 返回 成员 函数 C++      更新时间:2023-10-16

在某个生物物理模型的模拟框架内,我有一个C++类,它用一个需要返回PyArrayObject*的成员函数来实现我的模型。该类在头文件ng_networks.h中定义,其内容类似于:

#include <stdio.h>
#include <math.h>
#include <Python.h>
#include <numpy/arrayobject.h> /* Numpy as seen from C */
struct ngn{...};  /* Structure of model parameter */  
class ngn_rate{   /* Model Class */
int NEQ;
ngn pars;
double *y,*dy;
public:
ngn_rate();   // Constructor
~ngn_rate();  // Destructor
PyObject* bifsystem();
}
ngn_rate::ngn_rate(){
// Set ngn pars default values 
...
// Allocate memory for model variables and RHS of equations
y = (double*)calloc(NEQ,sizeof(double));
dy = (double*)calloc(NEQ,sizeof(double));
}
ngn_rate::~ngn_rate{
free(y);
free(dy);
}
PyObject* ngn_rate::bifsystem(){
long int NUMEL = NEQ; // PyArray creation function requires (long int*)
// Does some operations on y,dy
...
// Build PyObject* from dy
// Create Python Array Object...
PyObject* out_array = PyArray_SimpleNew(1,&NUMEL,NPY_DOUBLE);
// ... and make C pointer to point to it
double* dy_aux = (double*)((PyArrayObject*)out_array)->data;
// Copy dy into PyObject out_array
for(int i=0;i<NUMEL;i++) dy_aux[i] = dy[i];
return out_array; 
}

正如您可能猜测的那样,这个类最终是从Python模块调用的。在这方面,我使用scipy.weave将我的C代码与Python接口。因此,调用Python模块看起来像:

def ngn_rate_py():
support_code = """
#include <Python.h>
#include "ng_networks.h" 
"""
source_files = [...] # A list of C/CPP source file names
libs = ['m']
dirs = [...]         # A list of #include dirs
# My C code to interface with Python
code = """
//Initialize Model 
ngn_rate network();
//Return dy_dt
return_val = network.bifsystem();
"""
vars = []
dy   = weave.inline(code,
vars,
support_code = support_code,
sources = source_files,
libraries = libs,
library_dirs = dirs,
include_dirs = dirs,
runtime_library_dirs = dirs,
type_converters = converters.blitz,
compiler = 'gcc',
extra_compile_args = ['-std=c++11'],
force = 1)
return dy

不幸的是,当我运行上述模块时,它会产生分段故障。经过调试和尝试,我发现问题是由ng_networks.hPyObject* out_array的初始化引起的。事实上,当我在weave中的C代码中创建PyObject*时,它非常完美:也就是说,我修改ngn_rate::bifsystem()类成员函数,使其返回double*,然后在编织接口中从后者构建PyObject*

class ngn_rate{   /* Model Class */
...
public:
...
double* bifsystem();
}
double* ngn_rate::bifsystem(){
long int NUMEL = NEQ; // PyArray creation function requires (long int*)
// Does some operations on y,dy
...
return dy;
}

然后在weave接口中:

def ngn_rate_py():
support_code = """
#include <Python.h>
#include "ng_networks.h" 
"""
code = """
//Initialize Model 
ngn_rate network();
//Create temporary dy  
double *dy = network.bifsystem();
//Create PyObject*
PyObject* out_array = PyArray_SimpleNew(1, &NUMEL, NPY_DOUBLE);
double* dy_aux = (double*) ((PyArrayObject*) out_array)->data;
//Assign dy to PyObject            
for(int i=0;i<NUMEL;i++) dy_aux[i]=dy[i];
return_val = out_array;

我不明白为什么上面的工作,而如果我让PyObject*由我的类返回,我会得到一个分段错误。值得注意的是,在其他一些代码中,我只有一个静态/非成员C函数,它返回了一个PyObject*,当从weave调用时,它运行得很好。所以我的猜测是,在C类对象中使用PyObject*存在一些问题。但我不知道是什么。尽管我可以使用上面的代码在weave接口中创建PyObject*,但为了可移植性,我宁愿让我的类ngn_rate直接提供它。

提前感谢您的反馈。

M

如注释所建议的那样,分段错误的解决方案是确保初始化Python-C API和Numpy数组。这可以通过Py_Initialize()import_array()宏来完成,它们适时地包含在类ngn_rate的构造函数中,即

#include <Python.h>
#include <numpy/arrayobject.h>
class ngn_rate{
...
public:
ngn_rate();
...
};
ngn_rate::ngn_rate{
Py_Initialize();
import_array();
...
}