使用swig进行python列表输入和输出

时间:2018-03-21 05:13:00

标签: python c swig typemaps

我正在使用SWIG为某些功能构建一个Python模块。基于C代码的评估。

我需要的主要功能定义如下:

void eval(double *x, int nx, int mx, double *f, int func_id)

目标python函数应该是:

value_list = module.eval(point_matrix, func_id)

此处, eval 将调用基准函数并返回其值。 func_id 是要调用的函数eval的id, nx 是函数的维度, mx 是要求的点数评价。

实际上,我并不清楚SWIG如何在类型图之间传递值(例如,temp $ argnum,为什么总是使用$ argnum?)。但是通过查看包装代码,我完成了typemap.i文件:

%module cec17

%{
#include "cec17.h"
%}

%typemap(in) (double *x, int nx, int mx) (int count){
        if (PyList_Check($input)) {
                $3 = PyList_Size($input);
                $2 = PyList_Size(PyList_GetItem($input, 0));
                count = $3;
                int i = 0, j = 0;
                $1 = (double *) malloc($2*$3*sizeof(double));
                for (i = 0; i < $3; i++){
                        for (j = 0; j < $2; j++){
                                PyObject *o = PyList_GetItem(PyList_GetItem($input, i), j);
                                if (PyFloat_Check(o))
                                        $1[i*$2+j] = PyFloat_AsDouble(o);
                                else {
                                        PyErr_SetString(PyExc_TypeError, "list must contrain strings");
                                        free($1);
                                        return NULL;
                                }
                        }
                } 
        } else {
                PyErr_SetString(PyExc_TypeError, "not a list");
                return NULL;
        }
}

%typemap(freearg) double *x {
        free((void *) $1);
}

%typemap(in, numinputs=0) double *f (double temp) {
        $1 = &temp;
}

%typemap(argout) double *f {
        int i = 0;
        int s = count1;
        printf("pass arg %d", s);
        $result = PyList_New(0);
        for (i = 0; i < s; i++){
                PyList_Append($result, PyFloat_FromDouble($1[i]));
        }
}

void eval(double *x, int nx, int mx, double *f, int func_num);

然而,奇怪的事情发生了。通常,我测试30维函数。当评估少于10个点(mx <10)时,模块工作正常。评估更多点时,会发生错误:

[1] 13616 segmentation fault (core dumped) python test.py

我很确定问题不在c代码中,因为唯一的地方是&#39; mx&#39;发生在&#39; for-loop&#39;其中是每个点的评估。

我还尝试阅读包装代码和调试,但我无法找到问题所在。以下是SWIG生成的包装代码的一部分,我添加了一个&#39; printf&#39;线。在错误发生之前,甚至不会打印此字符串。

#ifdef __cplusplus
extern "C" {
#endif
SWIGINTERN PyObject *_wrap_eval(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
  PyObject *resultobj = 0;
  double *arg1 = (double *) 0 ;
  int arg2 ;
  int arg3 ;
  double *arg4 = (double *) 0 ;
  int arg5 ;
  int count1 ;
  double temp4 ;
  int val5 ;
  int ecode5 = 0 ;
  PyObject * obj0 = 0 ;
  PyObject * obj1 = 0 ;
  printf("check point 0");  
  {
    arg4 = &temp4;
  }
  if (!PyArg_ParseTuple(args,(char *)"OO:eval",&obj0,&obj1)) SWIG_fail;
  {
    if (PyList_Check(obj0)) {
      arg3 = PyList_Size(obj0);
      arg2 = PyList_Size(PyList_GetItem(obj0, 0));
      count1 = arg3;
      int i = 0, j = 0;
      arg1 = (double *) malloc(arg2*arg3*sizeof(double));
      for (i = 0; i < arg3; i++){
        for (j = 0; j < arg2; j++){
          PyObject *o = PyList_GetItem(PyList_GetItem(obj0, i), j);
          if (PyFloat_Check(o))
          arg1[i*arg2+j] = PyFloat_AsDouble(o);
          else {
            PyErr_SetString(PyExc_TypeError, "list must contrain strings");
            free(arg1);
            return NULL;
          }
        }
      } 
    } else {
      PyErr_SetString(PyExc_TypeError, "not a list");
      return NULL;
    }
  }
  ecode5 = SWIG_AsVal_int(obj1, &val5);
  if (!SWIG_IsOK(ecode5)) {
    SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "eval" "', argument " "5"" of type '" "int""'");
  } 
  arg5 = (int)(val5);
  eval(arg1,arg2,arg3,arg4,arg5);
  resultobj = SWIG_Py_Void();
  {
    int i = 0;
    int s = count1;
    resultobj = PyList_New(0);
    for (i = 0; i < s; i++){
      PyList_Append(resultobj, PyFloat_FromDouble(arg4[i]));
    }
  }
  return resultobj;
fail:
  return NULL;
}

感谢您阅读此内容。 :)

问题似乎有点单调乏味。也许你可以告诉我如何编写正确的typemap.i代码。

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

我不确定你的评估函数应该做什么,所以我猜了一下并为它实现了一个包装器。我用value_list = module.eval(point_matrix, func_id)表示你想要返回一个对每行数据点评估一些函数的结果列表,并提出以下内容。我改变的事情:

  1. 使用Python列表中的数字列表替换第一个四个参数。
  2. f中的结果空间被malloced。
  3. 要接受float以外的其他数字类型,每个值都会调用PyFloat_AsDouble,并调用PyErr_Occurred以查看它是否无法转换。
  4. freearg typemap现在可以释放这两个分配。
  5. argout typemap现在正确处理f输出参数。
  6. 我添加了一个示例eval实现。

    %module cec17
    
    %typemap(in) (double *x, int nx, int mx, double* f) %{
        if (PyList_Check($input)) {
            $3 = PyList_Size($input);
            $2 = PyList_Size(PyList_GetItem($input, 0));
            $1 = malloc($2 * $3 * sizeof(double));
            $4 = malloc($3 * sizeof(double));
            for (int i = 0; i < $3; i++) {
                for (int j = 0; j < $2; j++) {
                    PyObject *o = PyList_GetItem(PyList_GetItem($input, i), j);
                    double tmp = PyFloat_AsDouble(o);
                    if(PyErr_Occurred())
                        SWIG_fail;
                    $1[i * $2 + j] = PyFloat_AsDouble(o);
                }
            }
        } else {
            PyErr_SetString(PyExc_TypeError, "not a list");
            return NULL;
        }
    %}
    
    
    %typemap(freearg) (double *x, int nx, int mx, double* f) %{
        free($1);
        free($4);
    %}
    
    %typemap(argout) (double *x, int nx, int mx, double* f) (PyObject* tmp) %{
        tmp = PyList_New($3);
        for (int i = 0; i < $3; i++) {
            PyList_SET_ITEM(tmp, i, PyFloat_FromDouble($4[i]));
        }
        $result = SWIG_Python_AppendOutput($result, tmp);
    %}
    
    %inline %{
        void eval(double *x, int nx, int mx, double *f, int func_num)
        {
            for(int i = 0; i < mx; ++i) {
                f[i] = 0.0;
                for(int j = 0; j < nx; ++j)
                    f[i] += x[i*nx+j];
            }
        }
    %}
    
  7. 输出:

    >>> import cec17
    >>> cec17.eval([[1,2,3],[4,5,6]],99)
    [6.0, 15.0]
    

    可以改进错误检查。例如,检查序列而不是列表。只检查外部列表它实际上是一个列表,因此如果[1,2,3]是第一个参数而不是嵌套列表,它将无法正常运行。没有检查所有子列表的大小是否相同。

    希望这会有所帮助。如果有什么不清楚,请告诉我。