C++Builder 的 32 位编译器 bcc32 默认情况下使用 cdecl 调用约定创建共享库,并在导出函数前添加下划线前缀, 例如'_函数名称'。另一方面,Visual Studio 不为导出函数添加前缀。
Python 在导入 pyd 模块时需要一个名为 PyInitialize_modulename 的函数。由于 bcc32 在此函数前面添加了下划线,将其渲染为 _PyInitialize_modulename,Python 将无法导入 bcc32 创建的模块。
使用 CMake,有谁知道如何在编译/创建 pyd 模块时添加模块定义文件 .def 以将带前缀的函数别名为“无前缀”函数?
更新。回应下面 Lebeau 先生的回答(作为评论); 这个问题不是关于 CType 的,而是关于 SWIG 的。 CTypes 允许以简单的方式包装 C/C++ DLL,但主要处理 C 结构和 POD 数据。
另一方面,Swig 允许客户端在 Python 中获取面向对象的对象,类似于从 C++ DLL 导出的对象。 Swig 可以在处理 C++ header 时执行此操作。Swig 确实创建了一个 C++ 文件,该文件被编译为 .pyd 文件(本质上是一个 DLL,如前所述)。 Python 在尝试将其作为模块加载时查找的第一个导出函数是 Pyinit_MyModule (Python 3)。如前所述,使用 C++ Builder 时,此函数将导出为 _Pyinit_MyModule。问题是 Swig 导出了这个函数,而我作为 Swig 的客户端无法更改这个函数的调用约定(据我所知)。
我相信我最初认为Python需要__stdcall来实现这个函数是错误的,因为VS默认使用cdecl,但没有添加'_',而且它工作得很好。
但是,设置编译器标志来抑制下划线也不起作用,因为 Python 导入库中的某些函数会变得不可见,并成为无法解析的外部函数。所以也许这个问题比乍看起来更复杂。但我想这与调用约定无关。
最佳答案
cdecl
只是每个 C++ 编译器使用的典型默认调用约定。如果您想使用 stdcall
来代替,您当然可以这样做(Python 期望使用 stdcall
是有道理的,因为 Win32 API 使用的就是这个)。
在模块的 C++ 代码中,您可以在导出的函数声明中显式使用 __stdcall
。
或者,您可以在 C++Builder 的项目设置中更改默认调用约定,然后在函数声明中省略任何调用约定。
现在,回答您的问题 - 在导入 pyd 模块时,如果该模块使用 cdecl
,请使用 ctypes.cdll
调用其函数。如果模块使用 stdcall
,请改用 ctypes.windll
。 Python 文档对此进行了介绍:
https://docs.python.org/3/library/ctypes.html#loading-dynamic-link-libraries
16.16.1.1. Loading dynamic link libraries
ctypes
exports thecdll
, and on Windowswindll
andoledll
objects, for loading dynamic link libraries.You load libraries by accessing them as attributes of these objects.
cdll
loads libraries which export functions using the standardcdecl
calling convention, whilewindll
libraries call functions using thestdcall
calling convention.oledll
also uses thestdcall
calling convention, and assumes the functions return a WindowsHRESULT
error code. The error code is used to automatically raise anOSError
exception when the function call fails.
https://docs.python.org/3/faq/windows.html#is-a-pyd-file-the-same-as-a-dll
Is a *.pyd file the same as a DLL?
Yes,
.pyd
files are dll’s, but there are a few differences. If you have a DLL named foo.pyd, then it must have a functionPyInit_foo()
. You can then write Python “import foo”, and Python will search for foo.pyd (as well as foo.py, foo.pyc) and if it finds it, will attempt to callPyInit_foo()
to initialize it. You do not link your .exe with foo.lib, as that would cause Windows to require the DLL to be present.
关于python-3.x - 是否可以使用 C++Builder 和 CMake 创建 Python 模块?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49909832/