一、问题发生环境
python可以把C/C++代码编译并打包为pyd模块,从而可以使python脚本直接调用C/C++模块功能。
我在执行python setup.py build_ext --inplace时遇到了缺失cl.exe的错误提示,然后用pip安装了cl。
再次编译,提示cl: error: no such option: -I,改变cl版本仍然不行,百思不得其解。
二、解决办法
后来意识到C/C++模块的编译实际上还是python调用专门的C/C++编译器进行编译的,在另一台电脑上全新的环境上运行,发现系统默认执行的是Microsoft Visual C++ (14.0以上版本)下的cl来编译C/C++,而不是python下的cl。
Microsoft C++ 生成工具 - Visual Studio
下Microsoft生成工具,具体安装方法可以搜索“解决报错 Microsoft Visual C++ 14.0 is required”,网上有很多相关文档。
之后就可以正常调用MSVC的cl来编译打包pyd啦。
三、python distutils使用方法
下面记录一下python distutils的使用方法。
创建一个test文件夹。
编辑一个test.c,随便写个Hello world就麻烦:
include <Python.h>,以PyMODINIT_FUNC为标志写一个Python Module init函数来Create一个Python Module “test”。
其中,PyObject、PyModule_Create等都定义在Python.h中,并且Python.h已经include了stdio.h。
PyMODINIT_FUNC PyInit_test(void)
{PyObject *m;m = PyModule_Create(&test);if (m == NULL){return Py_BuildValue("");}return m;
}
定义Python Module “test”,test_methods指向test module的方法。
PyMethodDef test_methods[] =
{{"hello", hello, METH_VARARGS, "print a hello world"},{NULL, NULL, 0, NULL}
};struct PyModuleDef test =
{PyModuleDef_HEAD_INIT,"test", // name of module /NULL, // module documentation, may be NULL /-1, // size of per-interpreter state of the module, or -1 if the module keeps state in global variables. /test_methods // A pointer to a table of module-level functions, described by PyMethodDef values. Can be NULL if no functions are present. /
};
终于可以写Hello World了,注意返回值类型应为PyObject*,直接把全部代码放上来吧。
#include <Python.h>PyObject* hello()
{printf("Hello World!");return Py_True;
}PyMethodDef test_methods[] =
{{"hello", hello, METH_VARARGS, "print a hello world"},{NULL, NULL, 0, NULL}
};struct PyModuleDef test =
{PyModuleDef_HEAD_INIT,"test", // name of module /NULL, // module documentation, may be NULL /-1, // size of per-interpreter state of the module, or -1 if the module keeps state in global variables. /test_methods // A pointer to a table of module-level functions, described by PyMethodDef values. Can be NULL if no functions are present. /
};PyMODINIT_FUNC PyInit_test(void)
{PyObject *m;m = PyModule_Create(&test);if (m == NULL){return Py_BuildValue("");}return m;
}
在test目录下创建一个setup.py,编辑如下:
from distutils.core import setup, Extensionmodule1 = Extension('test',sources = ['test.c'])setup (name = 'test',version = '1.0',description = 'test extention',ext_modules = [module1])
name、version等都不必要,只有ext_modules必要。
执行python setup.py build_ext --inplace,生成test.cp38-win_amd64.pyd,pyd就是python dll,可以直接被python脚本调用的。

最后写个python脚本test.py调用一下test模块测试一下:
import testtest.hello()
输出Hello World!,完美。