带有 Python3.5 的 Cython:将字符串列表转换为 char**

Cython with Python3.5: convert list of string to char**

我正在尝试将字符串列表传递给使用 Cython 将 char** 作为参数的 C++ 函数。

我尝试了其他的解决方案我不记得了,但我主要尝试了以下两个选项:

  1. 使用从here, which is basically the same answer than here中提取的转换函数,但在g++编译时引发错误

    cy_wrapper.cpp: In function 'char** _pyx_f_10cy_wrapper_to_cstring_array(PyObject*)':
    cy_wrapper.cpp:1223:44: error 'PyString_AsString' was not declared in this scope
        __pyx_t_5 = PyString_AsString(__pyx_t_4); if (unlikely(__pyx_t_5 == ((char *)NULL))
    
  2. 使用python字符串的编码方法,如中所建议,但答案并不令人满意,因为它引发了错误:

    cy_wrapper.pyx:14:35: Storing unsafe C derivative of temporary Python reference
    

我查看了 ~/.local/lib/python3.5/site-packages/Cython/Includes/cpython 的库文件,发现文件 string.pxd 包含我需要的函数 PyString_AsString

为什么没有找到?如果没有可能使用它,是否有解决方法?

我正在 arm64 arch 上开发 Ubuntu 16.04.4 LTS(tegra 内核)。

我的cy_wrapper.pyx就是这样:

from cpython.string cimport PyString_AsString
from libc.stdlib cimport malloc

cdef extern:
    cdef cppclass imageNet:
        imageNet* Create(int argc, char** argv)

cdef char** to_cstring_array(list_str):
    cdef char** ret = <char **>malloc(len(list_str) * sizeof(char *))
    for i in range(len(list_str)):
        ret[i] = PyString_AsString(list_str[i])
    return ret

cdef class PyImageNet:
    cdef imageNet* c_net
    def Create(self, argc, kwargs):
        cdef char** c_argv = to_cstring_array(kwargs)
        return PyImageNetFactory(self.c_net.Create(argc, c_argv))

cdef object PyImageNetFactory(imageNet* ptr):
    cdef PyImageNet py_obj = PyImageNet()
    py_obj.c_net = ptr
    return py_obj

我的setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
      cmdclass = {"build_ext": build_ext},
      ext_modules = [
          Extension("cy_wrapper",
                    sources=["cy_wrapper.pyx"],
                    libraries=["shared_inference"],
                    language="c++",
                    extra_compile_args=["-O3", "-Wall"],
                    extra_link_args=["-L../build/"]
                    )
          ]
)

Ofc libshared_inference.so 位于 ../build/ 并定义 class imageNet.

对于那些感兴趣的人,这里是我如何将我的 string 列表转换为 char** (我忘了在我的问题中提到我的方法是静态的,但这并不重要我的解决方案),希望这会对某人有所帮助。

# cy_wrapper.pyx
from libc.stdlib cimport malloc, free

cdef extern:
    cdef cppclass imageNet:
        @staticmethod
        imageNet* Create(int argc, char** argv)

cdef class PyImageNet:
    cdef imageNet* c_net
    @staticmethod
    def Create(args):
        # Declare char**
        cdef char** c_argv
        # Allocate memory
        c_argv = <char**>malloc(len(args) * sizeof(char*))
        # Check if allocation went fine
        if c_argv is NULL:
            raise MemoryError()
        # Convert str to char* and store it into our char**
        for i in range(len(args)):
            args[i] = args[i].encode()
            c_argv[i] = args[i]
        # Grabbing return value
        cdef imageNet* c_tmp_net = imageNet.Create(len(args), c_argv)
        # Let him go
        free(c_argv)
        # Return python-compatible value
        return PyImageNetFactory(c_tmp_net)

cdef object PyImageNetFactory(imageNet* ptr):
    cdef PyImageNet py_obj = PyImageNet()
    py_obj.c_net = ptr
    return py_obj

测试代码:

# test.py
import cy_wrapper
args = ["str1", "str2"]
net = cy_wrapper.PyImageNet.Create(args)