使用 cython 包装 c++ DLL:所有字符串均为空

Wrapping a c++ DLL using cython: all strings are empty

本文关键字:字符串 cython 包装 c++ DLL 使用      更新时间:2023-10-16

我正在尝试使用cython包装用c ++编写的DLL。最后,我将无法访问源代码,所以C++源代码本身不能在编译包装器的过程中使用,.dll.lib.h。一切都编译得很好。但是,我立即发现字符串表现不佳。 例如,dll返回简单字符串的函数无法正常工作:cython 代码总是得到空字符串。 我在窗口 7 上。我已经验证了以下内容。

  • DLL 已由 Visual Studio 2015(即 v 1900)使用 2015 工具集在调试中构建模式。我尝试使用/MTd 和/MDd 标志进行编译。生成的 .lib 和.dll文件是与下面的所有其他文件放在同一个文件夹中。
  • 我的python 3.5发行版也是由VS 2015构建的,用于32位。

    $ python -iu
    Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] on win32
    
  • 当我使用包含返回字符串的此类函数的虚拟 c++ 源代码进行编译时,它只是工作。使用 dll 它不起作用。

setup.py:

from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize(
    "test.pyx",                 # our Cython source
))

test.pyx:

# distutils: language = c++
# distutils: libraries = MyDLL
# distutils: include_dirs = .
# distutils: library_dirs = .
from libcpp.string cimport string
cdef extern from "MyDLL.h" namespace "somenamespace":
    int ReturnSomeInt()
    string ReturnSomeString()
def run():
    cdef string s = string(b'abcdefg')
    print(s)
    cdef int i = ReturnSomeInt()
    print(i)
    cdef string problem = ReturnSomeString()
    print(problem)

MyDLL.h:

__declspec(dllexport) int ReturnSomeInt();
__declspec(dllexport) std::string ReturnSomeString();

用于编译 MyDLL 的C++代码片段:

__declspec(dllexport) int ReturnSomeInt() { return 42; }
__declspec(dllexport) std::string ReturnSomeString() { cout << "debug..." << endl; return "Hello world!"; }

main.py:

from test import run
run()

我使用命令编译

$ python setup.py build_ext --inplace --force && python -u main.py

运行此打印

b'abcdefg'
42
debug... # printed by the dll function ReturnSomeString()
b'' # Should've printed: b'Hello World!'

我们可以验证 MyDLL 中的 ReturnSomeString() 是否确实被调用,因为它会向 stdout 发送一些文本。

我没有检查什么?

多亏了@hakala指出的这个答案,我发现调试/发布模式很重要。我没想到可以。我引用:

C++标准库有自己的一组 ABI 问题。没有 保证给定的 STL 类型在内存中的布局方式相同, 也不能保证给定的 STL 类具有相同的大小 一个实现到另一个实现(特别是,调试版本可能会将 将额外的调试信息转换为给定的 STL 类型)。因此,任何 STL 容器必须先解压缩为基本类型,然后才能 越过 DLL 边界并在另一侧重新打包。

实际上,在发布模式下编译 DLL 使 OP 中的示例起作用。