如何使用正确的本机调用约定编译 C 扩展?

How is a C extension compiled with the correct native calling convention?

假设我们正在用 C 编写一个 CPython 扩展模块。

定义扩展时,我们需要提供一个PyMODINIT_FUNC PyInit_<module_name>(void)。此声明中未指定本机调用约定。

无论是 Linux 还是 Windows,扩展开发人员如何确保 PyInit_ 函数使用正确的调用约定进行编译,以便解释器在期间成功调用它运行? Python headers 中有什么魔法可以帮我们解决这个问题吗?或者别的什么?

同样的问题也适用于扩展模块中包含的本机函数,并暴露给包装为函数 objects 的 Python。我们如何确保以与 Python 解释器调用约定兼容的方式编译它们?

默认调用约定由编译器自动制定。 PyMODINIT_FUNCvoid(在 Python 2 中)或 PyObject *(在 Python 3 中),仅此而已。

Python 动态模块加载器使用平台的默认约定(并不是说它对此有任何管辖权;它属于平台)。

您实际上是在写 PyMODINIT_FUNC PyInit_<module_name>(void)。现在 PyMODINIT_FUNC 是一个宏,可以扩展到由 pyport.h 中的各种 #ifdef 控制的不同事物。在 Github 修订 Python 3.9 开发版本中的当前 HEAD 修订中有这 6 个定义:

#define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject*
#define PyMODINIT_FUNC PyObject*
#define PyMODINIT_FUNC extern "C" Py_EXPORTED_SYMBOL PyObject*
#define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject*
#define PyMODINIT_FUNC extern "C" Py_EXPORTED_SYMBOL PyObject*
#define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject*

即它扩展为 PyObject * 作为 return 类型,但如果使用 C++ 编译器编译以确保针对此符号的 C 链接,它也扩展为 extern "C" 包括Py_EXPORTED_SYMBOL 在某些平台上。

Py_EXPORTED_SYMBOLexports.h 中定义为

之一
#define Py_EXPORTED_SYMBOL __declspec(dllexport)
#define Py_EXPORTED_SYMBOL __attribute__ ((visibility ("default")))
#define Py_EXPORTED_SYMBOL

None 其中指定了调用约定,而不是让函数默认为 cdecl 约定,因为它们应该... - 还请注意,调用约定大多只在 Windows 中.