使用SWIG和Python / C API来包装一个获取C ++类实例的Python列表的函数

时间:2014-10-09 10:24:50

标签: python c++ swig

我想包装一个C ++例程,它接受一个类实例数组。我无法与SWIG合作,并希望能提供任何帮助。我试图通过一个简单的例子将这个问题归结为其本质。

标题test.h定义如下:

/* File test.h */
#include <stdlib.h>
#include <stdio.h>

class Test {
  private:
    int my_value;
  public:
    Test(int value);
    void printTest();
};

void print_tests(int num_tests, Test** tests);

实施在test.cpp下面定义:

/* File test.cpp */
#include "test.h"

void print_tests(int num_tests, Test** tests) {
  for (int i=0; i < num_tests; i++)
    tests[i]->printTest();
}

Test::Test(int value) { 
  my_value = value; 
}

void Test::printTest() { 
  printf("my value is %d\n", my_value); 
}

我编写了一个SWIG接口文件test.i来尝试容纳此例程,以便我可以传入Test类实例的Python列表:

%module test

%{
  #define SWIG_FILE_WITH_INIT
  #include "test.h"
%}

%typemap(in) (int num_tests, Test** tests) {

  if (!PyList_Check($input)) {
    PyErr_SetString(PyExc_ValueError, "Expected a Python list of Tests");
    return NULL;
  }

  $1 = PySequence_Length($input);  // num_tests
  $2 = (Test**) malloc(($1) * sizeof(Test*)); // tests

  /* Loop over tests */
  for (int i = 0; i < $1; i++) {
    /* Extract the value from the list at this location */
    PyObject* o = PyList_GetItem($input,i);
    $2[i] = (Test*) o;
  }
}

%include "test.h"

我打包例程并编译SWIG生成的包装器代码,并将其作为共享库链接,如下所示:

> swig -python -c++ -o test_wrap.cpp test.i
> gcc -c test.cpp -o test.o -fpic -std=c++0x
> gcc -I/usr/include/python2.7 -c test_wrap.cpp -o test_wrap.o -fpic -std=c++0x
> g++ test_wrap.o test.o -o _test.so -shared -Wl,-soname,_test.so

然后我希望能够在Python中执行以下操作:

import test

test1 = test.Test(2)
test2 = test.Test(1)
test.print_tests([test1, test2, test1])

但是,如果我将其作为脚本example.py运行,我会收到以下输出:

> python example.py 
my value is 3
my value is 2
my value is 3

为什么我将23作为输出,而不是分配给1类构造函数的2Test?我相信这是我的SWIG接口文件传递指向SWIG Test类包装器实例而不是C ++ Test类实例的指针的问题,但我不确定。任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

我认为问题在于尝试直接将PyObject o转换为Test *。 我们不希望PyObject的指针是SwigPyObject,而Test*指针位于SwigPyObject内的某个位置。 你可以做的是重用SWIG_ConvertPtr(swig在生成的包装器中的方式):

%typemap(in) (int num_tests, Test** tests) {

  if (!PyList_Check($input)) {
    PyErr_SetString(PyExc_ValueError, "Expected a Python list of Tests");
    return NULL;
  }

  $1 = PySequence_Length($input);  // num_tests
  $2 = (Test**) malloc(($1) * sizeof(Test*)); // tests

  /* Loop over tests */
  for (int i = 0; i < $1; i++) {
    /* Extract the value from the list at this location */
    PyObject* o = PyList_GetItem($input,i);
    void *p1 = 0;
    SWIG_ConvertPtr(o, &p1,SWIGTYPE_p_Test, 0 |  0 );
    $2[i] = (Test*) p1;
  }
}