使用Swig将Eigen/C++封装到Python时出错
我在用SWIG封装一个小项目时遇到了麻烦,这个项目使用了Eigen(一个线性代数库)。我碰到了一个我不理解的Python错误,网上也找不到太多相关的信息——但我怀疑可能是C++的内存出了问题。我把问题简化成了一个小例子……不过不幸的是,这个例子还是比较长:
--- testfunc.cxx ----
#include "Eigen/Dense"
Eigen::VectorXd test(Eigen::MatrixXd data){
Eigen::VectorXd temp;
return temp;
}
--- testswig.i -----
%module testswig
%{
#define SWIG_FILE_WITH_INIT
#include "Eigen/Core"
#include <Python.h>
#include <numpy/arrayobject.h>
#include "testfunc.cxx"
%}
%init
%{
import_array();
%}
%include "numpy.i"
%typemap(out) Eigen::VectorXd
{
npy_intp dims[1] = {$1.size()};
PyObject* array = PyArray_SimpleNew(1, dims, NPY_DOUBLE);
double* data = ((double *)PyArray_DATA( array ));
for (int i = 0; i != dims[0]; ++i){
*data++ = $1.data()[i];
}
$result = array;
}
%typemap(in) Eigen::MatrixXd (Eigen::MatrixXd TEMP)
{
int rows = 0;
int cols = 0;
rows = PyArray_DIM($input,0);
cols = PyArray_DIM($input,1);
PyArrayObject* temp;
PyArg_ParseTuple($input, "O", &temp);
TEMP.resize(rows,cols);
TEMP.fill(0);
double * values = ((double *) PyArray_DATA($input));
for (long int i = 0; i != rows; ++i){
for(long int j = 0; j != cols; ++j){
// std::cout << "data " << data[i] << std::endl;
TEMP(i,j) = values[i*rows+j];
}
}
}
%include "testfunc.cxx"
--- setup.py ----
from distutils.core import setup, Extension
import numpy
numpyinclude = numpy.__file__[:-12] + 'core/include/'
testswig = Extension('_testswig',
sources=['testswig_wrap.cxx'],
include_dirs=['../', numpyinclude])
setup (name = 'testswig',
version = '0.1',
author = "NoName",
description = """ """,
ext_modules = [testswig],
py_modules = ["testswig"])
----- 构建过程 ------
我在一个文件夹里放了这三个文件,还有一个名为'Eigen'的文件夹,里面包含了Eigen的头文件。执行的命令是:
swig -c++ -python -I./ testswig.i
python setup.py install
------- 错误信息 ----------
然后我运行一个包含以下内容的Python文件:
import testswig
import numpy as np
print testswig.test(np.array([[2,3],[4,5]]))
结果出现了错误:“SystemError: new style getargs format but argument is not a tuple”。
注意几点: 1) 直接在Python解释器中运行相同的命令是没问题的。 2) 如果函数不返回Eigen::VectorXd,或者不接受Eigen::MatrixXd,程序就能正常工作。
谢谢你的时间。
1 个回答
3
在你的类型映射中,你有:
PyArrayObject *temp;
PyArg_ParseTuple($input, "O", &temp);
这个写法是不对的 - $input
是一个已经从参数中提取出来的 PyObject,但它此时还不是一个元组,所以你应该写:
PyArrayObject *temp=NULL;
if (PyArray_Check($input))
temp = (PyArrayObject*)$input;
要先确认它的类型是否正确,然后再进行转换。