Python - typemap中的SWIG不起作用

时间:2018-01-22 23:32:49

标签: python c++ python-3.x swig

我正在学习将c ++代码包装到Python模块中,而像int foo(int argc, char **argv);这样的函数需要typemap。例如,一个简单的c ++代码可以

#include <iostream>

int foo(int argc, char** argv) {
    std::cout << "Have " << argc << " arguments:" << std::endl;
    for (int i = 0; i < argc; ++i) {
        std::cout << argv[i] << std::endl;
    }
    return 0;
}

然后我按照SWIG教程here(34.9.2)编写了SWIG的接口文件:

%module Args
%{
extern int foo(int argc, char **argv);
%}

%typemap(in) (int argc, char **argv) {
  /* Check if is a list */
  if (PyList_Check($input)) {
    int i;
    $1 = PyList_Size($input);
    $2 = (char **) malloc(($1+1)*sizeof(char *));
    for (i = 0; i < $1; i++) {
      PyObject *o = PyList_GetItem($input,i);
      if (PyString_Check(o))
    $2[i] = PyString_AsString(PyList_GetItem($input,i));
      else {
    PyErr_SetString(PyExc_TypeError,"list must contain strings");
    free($2);
    return NULL;
      }
    }
    $2[i] = 0;
  } else {
    PyErr_SetString(PyExc_TypeError,"not a list");
    return NULL;
  }
}

%typemap(freearg) (int argc, char **argv) {
  free((char *) $2);
}

extern int foo(int argc, char **argv);

但是,在构建模块之后,Python中始终存在错误:

>>> import Args
>>> Args.foo(["foo","bar","spam","1"])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list must contain strings

在类型映射中,似乎$input确实作为Python列表被接收,但PyList_GetItem($input,i)出现了问题。我做错了吗?

提前致谢!

2 个答案:

答案 0 :(得分:1)

字节与字符

基本问题是Python 3字符串是字符字符串,而普通Python 2字符串和char*字节字符串。 (char*通常也被视为以空值终止。)因此,在Python 3中删除了PyString_Check,您必须以某种方式处理编码。

如果要接受Python 3 str对象,请使用PyUnicode函数检查和编码参数。否则,从Python传递bytes个对象:bytes literals以获取固定的ASCII字符串(如Args.foo([b"foo",b"bar",b"spam",b"1"])),或者str.encode的结果。

为什么代码编译

SWIG生成的包装代码与Python 2 or 3兼容。 即使使用-py3,它也是以Python 2样式编写的,并且由许多兼容性宏(来自pyhead.swg)支持:

#if PY_VERSION_HEX >= 0x03000000

/* ... */
#define PyString_Check(name) PyBytes_Check(name)
/* ... */

#endif

在现代的,主要是3世界中,SWIG最好使用bytesobject.h代替。它以另一种方式定义同义词,使字节串明显。

答案 1 :(得分:0)

我发现这个问题是谷歌搜索的结果,我收到了同样的错误

TypeError: list must contain strings

由于错误仅在升级到Python 3后出现,我能够通过以下更改来解决它

Args.foo([b"foo",n"bar",b"spam",b"1"])

b强制Python将字符串作为Python2字节字符串而不是Python3 unicodes字符串发送,以防这对其他人有用。

相关问题