cpp与python交互方法总结(一)
- cpp调用python
- 1. Python C API
- 1.1 调用基础python函数(无第三方库调用)
- 编写对应的python代码
- 编写调用python的c代码
- 编译运行
- 重要API介绍: 传参和获取返回值引用计数
- normal bugs:
- 1.2 调用python的第三方库
- 无特殊类型(numpy.array等)传入和返回
- 需要传入和返回numpy.array
- python调用cpp(编写C/C++ extensions)
- 无特殊数据交互
- cpp代码和wrapper
- 编写setup.py文件
- 编译安装运行
- numpy.array数据交互
- 处理numpy.array的cpp程序
- setup.py
- 编译运行
cpp调用python
1. Python C API
1.1 调用基础python函数(无第三方库调用)
调用按照数据交互分成以下几种
- 无参数,无输入的函数 do_nothing()
- 基础类型参数,单返回值 c=add(a, b)
- 基础类型参数,多返回值 c,d = add_sub(a, b)
- tuple/list输入,tuple/list返回值 list_obj=haha_merge_list(list_a, list_b)
编写对应的python代码
test1.py
def do_nothing():
print("do_nothing")
def add(a, b):
return a+b
def add_sub(a, b):
return a+b, a-b
def haha_merge_list(a, b):
print("print input in python", a, b)
c = list(a)+list(b)+["haha"]
return c
编写调用python的c代码
test1.cpp
#include <iostream>
#include <Python.h>
#include<string>
using namespace std;
/*
!!!!! attention: C++调用python时,如果python代码有问题,C++不会报错,而是直接结束程序
*/
// 如果value不为true,则打印错误信息,同时结束程序
void myassert(bool value, const char* error_string){
if(!value){
printf("%s", error_string);
printf("\n");
exit(-1);
}
}
int main()
{
// 如果运行报错找不到encodings,需设置使用的python.exe的路径
// Py_SetPythonHome(L"/home/hui/ide/miniconda3//envs/torch101/bin/");
// Py_SetProgramName(L"cpp_invoke_python"); /* optional but recommended */
Py_Initialize();//使用python之前,要调用Py_Initialize();这个函数进行初始化
myassert(Py_IsInitialized(), "Initialized failed!");
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");//修改搜索python脚本路径
PyObject * pModule = PyImport_ImportModule("test1");// import test1
myassert(pModule != NULL, "test1 not found or got error while import test1");
// 0. 无参数 无输出 test.do_nothing()
PyObject * pFunc0 = PyObject_GetAttrString(pModule, "do_nothing");//这里是要调用的函数名
myassert(pFunc0 != NULL, "test1.do_nothing() not found!");
PyEval_CallObject(pFunc0, NULL);
Py_DECREF(pFunc0);
int ok;
// 1.调用多个int输入,单个int输出的函数 res = add(10, 12)
PyObject * pFunc1 = PyObject_GetAttrString(pModule, "add");//这里是要调用的函数名
myassert(pFunc1 != NULL, "test1.add() not found!");
PyObject* args1 = Py_BuildValue("ii", 10, 12); // 给python函数创建参数赋值,i表示int,ii表示两个int
PyObject* pRet1 = PyEval_CallObject(pFunc1, args1);
int res1;
ok = PyArg_Parse(pRet1, "i", &res1);///转换返回类型
myassert(ok, "parse return result error");
cout << "res: " << res1 << endl;
Py_DECREF(pFunc1);
Py_DECREF(args1);
Py_DECREF(pRet1);
// 2.调用多个输入,多个输出的函数 res_1, res_2 = add_sub(10, 12)
PyObject * pFunc2 = PyObject_GetAttrString(pModule, "add_sub");//这里是要调用的函数名
myassert(pFunc2 != NULL, "test1.add_sub() not found!");
PyObject* args2 = Py_BuildValue("ii", 10, 12); // 给python函数创建参数赋值
PyObject* pRet2 = PyEval_CallObject(pFunc2, args2);
int res2_1, res2_2;
ok = PyArg_ParseTuple(pRet2, "ii", &res2_1, &res2_2);///转换返回类型
myassert(ok, "parse return result error");
cout << "res: (" << res2_1 << "," << res2_2 << ")" << endl;
Py_DECREF(pFunc2);
Py_DECREF(args2);
Py_DECREF(pRet2);
// 3. list_object = haha_merge_list(a, b)
PyObject * pFunc3 = PyObject_GetAttrString(pModule, "haha_merge_list");//这里是要调用的函数名
myassert(pFunc3 != NULL, "test1.haha_merge_list() not found!");
PyObject* args3 = Py_BuildValue("(ii)[iii]", 10, 12, 1, 2, 3); // 给python函数创建参数赋值,(ii)(iii)表示有两个参数,每个参数是包含2个/3个int的tuple/list, ()/[]可以嵌套,也就是说可以输入多级tuple/list
PyObject* pRet3 = PyEval_CallObject(pFunc3, args3);
PyObject *pList;
ok = PyArg_Parse(pRet3, "O!", &PyList_Type, &pList); /// separately, the & in &PyList_Type is super important!, O表示是一个python object,!表示如果转化失败会报错
myassert(ok, "return result must be a list.");
// PyErr_SetString(PyExc_TypeError, "parameter must be a list.");
Py_DECREF(pFunc3);
Py_DECREF(args3);
// cout returned list
Py_ssize_t n = PyList_Size(pList);
int i;
PyObject *pItem;
cout << "[";
for (i = 0; i < n; i++) {
pItem = PyList_GetItem(pList, i);
if (PyLong_Check(pItem)) {
int data;
PyArg_Parse(pItem, "i", &data);
cout << data << ",";
}
else if (PyBytes_Check(pItem)) {
char* data;
PyArg_Parse(pItem, "s", &data);
cout << data << ",";
}
else if (PyUnicode_Check(pItem)) {
char* data;
PyArg_Parse(pItem, "s", &data);
cout << data << ",";
}
else {
PyTypeObject* type = Py_TYPE(pItem);
cout << type->tp_name << endl;
Py_DECREF(type);
PyErr_SetString(PyExc_TypeError, "element must be int or string.");
cout << "element must be int or string." << endl;
exit(-1);
}
}
cout << "]" << endl;
Py_DECREF(pList);
Py_DECREF(pRet3);
Py_DECREF(pModule);
Py_Finalize(); // 与初始化对应
return 0;
}
编译运行
# 有时候代码写错了,g++编译出错但不会报错,也不会生成可执行文件,所以为了确认的确生成了可执行文件,最好把上次生成的删掉
hui@hui-XPS-8920:~/github/cpp_python_test$ rm test1
hui@hui-XPS-8920:~/github/cpp_python_test$ g++ test1.cpp -o test1 -I /home/hui/ide/miniconda3/envs/torch101/include/python3.7m/ -L /home/hui/ide/miniconda3/envs/torch101/lib/ -lpython3.7m
hui@hui-XPS-8920:~/github/cpp_python_test$ ./test1
do_nothing
res: 22
res: (22,-2)
print input in python (10, 12) [1, 2, 3]
[10,12,1,2,3,haha,]
重要API介绍: 传参和获取返回值引用计数
Py_DECREF
Python使用引用计数器管理内存,所以在cpp里对于应用我们在每次使用完pyobject后,要手动的调用Py_DECREF(pyobject);修改引用计数。(对于第一次写测试例子的可以暂时先不管内存管理,不写这句不考虑内存泄漏也可以)
Py_BuildValue
将cpp类型的数据转化为PyObject*从而传入python。
PyArg_Parse/PyArg_ParseTuple
将python里的PyObject*数据转化为cpp类型的数据从而传入cpp。
解析时,对于单个format code的解析使用PyArg_Parse,对于多个format code的解析使用PyArg_ParseTuple,用错了会解析失败。
其中使用的format类型对应关系如下
Format Code | Python Type | C/C++ Type |
c | str | char |
s | str | char* |
z | str/None | char*/NULL |
S | str | PyStringObject |
i | int | int |
l | long | long |
d | float | double |
D | complex | Py_Complex* |
O | (any) | Py_Object* |
- | - | - |
(id) | (int, float) | int, double |
((id)[iii]) | ((int, float), [int, int, int]) | int, float, int, int,. int |
(xxx)/[xxx]表示xxx组成的tuple/list可以嵌套
x=c/s/z/S/i/I/d/D/O
y=x/(yx)/[yx]
比如下面例子里 Py_BuildValue和对应的python对象
Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hello") 'hello'
Py_BuildValue("y", "hello") b'hello'
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
Py_BuildValue("s#", "hello", 4) 'hell'
Py_BuildValue("y#", "hello", 4) b'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,)
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456]
Py_BuildValue("{s,s}", "abc", "def") {'abc', 'def'}
Py_BuildValue("{s:i,s:i}",
"abc", 123, "def", 456) {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)",
1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
normal bugs:
test.cpp:2:20: fatal error: Python.h: 没有那个文件或目录
1.2 调用python的第三方库
无特殊类型(numpy.array等)传入和返回
如果无特殊类型数据交互,那就和上面一样进行交互,不需要额外新的API
test_numpy1.py
import numpy as np
def inner_product(a4, b4):
return int((np.array(a4) * np.array(b4)).sum())
test_numpy1.cpp
#include <iostream>
#include <Python.h>
#include<string>
using namespace std;
// 如果value不为true,则打印错误信息,同时结束程序
void myassert(bool value, const char* error_string){
if(!value){
printf("%s", error_string);
printf("\n");
exit(-1);
}
}
int main()
{
Py_Initialize();//使用python之前,要调用Py_Initialize();这个函数进行初始化
myassert(Py_IsInitialized(), "Initialized failed!");
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");//这一步很重要,修改Python路径
PyObject * pModule = NULL;//声明变量
pModule = PyImport_ImportModule("test_numpy1");//这里是要调用的文件名hello.py
myassert(pModule != NULL, "test_numpy1 not found or got error while import test_numpy1");
int ok;
// 1.调用多个输入,单个输出的函数 res = add(10, 12)
PyObject * pFunc1 = PyObject_GetAttrString(pModule, "inner_product");//这里是要调用的函数名
myassert(pFunc1 != NULL, "test_numpy1.inner_product() not found!");
PyObject* args1 = Py_BuildValue("((iiii)(iiii))", 1, 2, 3, 4, 2, 3, 4, 5); // 给python函数创建参数赋值
PyObject* pRet1 = PyEval_CallObject(pFunc1, args1);
int res1;
ok = PyArg_Parse(pRet1, "i", &res1);///转换返回类型
myassert(ok, "parse return result error");
cout << "res: " << res1 << endl;
Py_DECREF(pFunc1);
Py_DECREF(args1);
Py_DECREF(pRet1);
Py_DECREF(pModule);
Py_Finalize(); // 与初始化对应
return 0;
}
编译运行
hui@hui-XPS-8920:~/github/cpp_python_test$ g++ test_numpy1.cpp -o test_numpy1 -I /home/hui/ide/miniconda3/envs/torch101/include/python3.7m/ -L /home/hui/ide/miniconda3/envs/torch101/lib/ -lpython3.7m
hui@hui-XPS-8920:~/github/cpp_python_test$ ./test_numpy1res: 40
需要传入和返回numpy.array
传递numpy数据主要依赖于一些numpy提供的c接口
test_opencv.py
import cv2 as cv
def read_img(img_path):
return cv.imread(img_path)
def scale_up(img, scale=2):
height, width = img.shape[:2]
dsize = (width*scale, height*scale)
big_img = cv.resize(img, dsize)
print("big_img[0] in python:", big_img[0])
return big_img
test_opencv.cpp
#include <iostream>
#include <Python.h>
#include<string>
#include <numpy/arrayobject.h>
using namespace std;
// 如果value不为true,则打印错误信息,同时结束程序
void myassert(bool value, const char* error_string){
if(!value){
printf("%s", error_string);
printf("\n");
exit(-1);
}
}
/**
1.create data in cpp, pass to python, return to cpp.
import API:
import_array: load numpy api
PyArray_SimpleNewFromData(len_shape, shape, dtype, data_array): 将数据转化为PyArrayObject* (numpy.array)
PyArray_NewLikeArray
PyArray_SHAPE(PyArrayObject*): 获得shape
PyArray_DATA(ret_array): 获得flatten的一维数据指针
*/
int main()
{
Py_Initialize();//使用python之前,要调用Py_Initialize();这个函数进行初始化
myassert(Py_IsInitialized(), "Initialized failed!");
import_array(); /* load numpy api */
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");//这一步很重要,修改Python路径
PyObject * pModule = NULL;//声明变量
pModule = PyImport_ImportModule("test_opencv");//这里是要调用的文件名hello.py
myassert(pModule != NULL, "test_opencv not found or got error while import test_opencv");
int ok;
// 1.create data in cpp, pass to python, return to cpp.
PyObject * pFunc1 = PyObject_GetAttrString(pModule, "scale_up");//这里是要调用的函数名
myassert(pFunc1 != NULL, "test_opencv.scale_up() not found!");
PyObject* args1 = PyTuple_New(2);
/// turn cpp array to numpy PyObject
unsigned char array[4][4][3] = {{{1, 1, 1}, {2, 2, 2}, {3, 3, 3}, {4, 4, 4}},
{{2, 2, 2}, {3, 3, 3}, {4, 4, 4}, {5, 5, 5}},
{{3, 3, 3}, {4, 4, 4}, {5, 5, 5}, {6, 6, 6}},
{{4, 4, 4}, {5, 5, 5}, {6, 6, 6}, {7, 7, 7}}};
npy_intp dims[] = {4, 4, 3};
PyObject* input = PyArray_SimpleNewFromData(3, dims, NPY_UINT8, array); // 输入的array可以是一维flatten的指针
PyTuple_SetItem(args1, 0, input);
PyTuple_SetItem(args1, 1, Py_BuildValue("i", 2));
PyObject* pRet1 = PyEval_CallObject(pFunc1, args1);
PyArrayObject *ret_array;
ok = PyArray_OutputConverter(pRet1, &ret_array);
myassert(ok, "parse return result error");
npy_intp* shape = PyArray_SHAPE(ret_array);
unsigned char* ret_array_data = (unsigned char*)PyArray_DATA(ret_array); // 返回获取的data是一维flatten的指针
cout << shape[0] << " " << shape[1] << " " << shape[2] << endl;
for(int i = 0; i < 24; i++) cout << int(ret_array_data[i]) << ",";
cout << endl;
Py_DECREF(pFunc1);
Py_DECREF(args1);
Py_DECREF(pRet1);
Py_DECREF(pModule);
Py_Finalize(); // 与初始化对应
return 0;
}
编译运行
hui@hui-XPS-8920:~/github/cpp_python_test$ rm test_opencv && g++ test_opencv test_opencv.cpp -I. -I/home/hui/ide/miniconda3/envs/torch101/include/python3.7m/ -I /home/hui/ide/miniconda3/envs/torch101/lib/python3.7/site-packages/numpy/core/include/ -L /home/hui/ide/miniconda3/envs/torch101/lib/ -lpython3.7m -std=c++11
In file included from /home/hui/ide/miniconda3/envs/torch101/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1832:0,
from /home/hui/ide/miniconda3/envs/torch101/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,
from /home/hui/ide/miniconda3/envs/torch101/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,
from test_opencv.cpp:4:
/home/hui/ide/miniconda3/envs/torch101/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning "Using deprecated NumPy API, disable it with " "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp]
#warning "Using deprecated NumPy API, disable it with " \
^
test_opencv.cpp: In function ‘int main()’:
test_opencv.cpp:24:3: warning: converting to non-pointer type ‘int’ from NULL [-Wconversion-null]
import_array(); /* load numpy api */
^
hui@hui-XPS-8920:~/github/cpp_python_test$ ./test_opencvbig_img[0] in python: [[1 1 1] [1 1 1]
[2 2 2]
[2 2 2]
[3 3 3]
[3 3 3]
[4 4 4]
[4 4 4]]
8 8 3
1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,
python调用cpp(编写C/C++ extensions)
注意 C extensions接口特定于CPython,并且extensions模块不适用于其他Python实现。在许多情况下,可以避免编写C extensions并保留对其他Python实现的可移植性。例如,如果您的用例正在调用C库函数或系统调用,则应考虑使用ctypes模块或cffi库,而不是编写自定义C代码。通过这些模块,您可以编写Python代码与C代码进行接口,并且在Python的实现之间比编写和编译C扩展模块更容易移植。
无特殊数据交互
下面编写一个调用cpp里system函数的python接口,名字叫spam
cpp代码和wrapper
spam.c
// 官方建议:define PY_SSIZE_T_CLEAN before including Python.h
#define PY_SSIZE_T_CLEAN
// 由于Python.h中包含一些预处理,所以最好把这个include写在最前面
#include <Python.h>
/**
定义C函数 PyObject* (PyObject*, PyObject*)=>
注册函数 PyMethodDef
注册模块 PyModuleDef
创建模块构造函数 PyInit_${module_name}
*/
static PyObject *SpamError;
// 使用PyArg_ParseTuple/PyArg_ParseTupleAndKeywords转换python传入的对象
// 使用PyLong_FromLong/Py_BuildValue转化为python对象传入python
static PyObject* spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
if (sts < 0) {
PyErr_SetString(SpamError, "System command failed");
return NULL;
}
/*
如果C函数返回值时void,那么需要如下返回形式(返回None)
Py_RETURN_NONE;
或
Py_INCREF(Py_None);
return Py_None;
*/
return PyLong_FromLong(sts);
}
static PyMethodDef SpamMethods[] = {
// METH_VARARGS. 一种标志,告诉解释器这将用于c函数的调用
// 如果传递的是可以通过 PyArg_ParseTuple处理的tuple(args)类型的python参数,则使用METH_VARARGS
// 如果传递的是可以通过PyArg_ParseTupleAndKeywords处理的kwargs类型的python参数,则使用METH_KEYWORDS
{"system", spam_system, METH_VARARGS, "execute a shell command."},
{NULL, NULL, 0, NULL}
};
static PyModuleDef spammodule = {
PyModuleDef_HEAD_INIT,
"spam", // module name
"spam: execute a shell command", // doc, can be NULL
-1, // size of per-interpreter state of the module,
// or -1 if the module keeps state in global variables.
SpamMethods
};
// 模块的初始化函数,必须名为PyInit_${module_name}
// PyMODINIT_FUNC: define PyMODINIT_FUNC extern "C" __declspec(dllexport) PyObject*
// 该函数在打开python时不会自动调用,如果想要创建自动调用的内建函数,
// 需使用PyImport_AppendInittab()添加到PyImport_Inittab表中
PyMODINIT_FUNC
PyInit_spam(void)
{
PyObject *m;
m = PyModule_Create(&spammodule);
if (m == NULL)
return NULL;
SpamError = PyErr_NewException("spam.error", NULL, NULL);
Py_XINCREF(SpamError);
if (PyModule_AddObject(m, "error", SpamError) < 0) {
Py_XDECREF(SpamError);
Py_CLEAR(SpamError);
Py_DECREF(m);
return NULL;
}
return m;
}
编写setup.py文件
spam_setup.py
from distutils.core import setup, Extension
module1 = Extension('spam',
# define_macros = [('MAJOR_VERSION', '1'), ('MINOR_VERSION', '0')],
# include_dirs = ['/usr/local/include'],
# libraries = [''],
# library_dirs = ['/usr/local/lib'],
sources = ['spam.c'])
setup (name = 'spam',
version = '1.0',
description = 'This is a spam package',
author = 'Martin v. Loewis',
author_email = 'martin@v.loewis.de',
url = 'https://docs.python.org/extending/building',
long_description = '''This is really just a spam package.''',
ext_modules = [module1])
编译安装运行
# 编译生成.o和.so文件
# python setup_spam.py build --inplace
python setup_spam.py build_ext --inplace
# (非必须)清楚.o的临时文件
python setup_spam.py clean
# (非必须)在python的lib目录下建立软连接到编译生成的.so
# python setup_spam.py install
运行
>>> import spam
>>> spam.system("ls -l")
总用量 28
-rw-rw-r-- 1 hui hui 632 8月 13 16:45 setup.py
-rw-rw-r-- 1 hui hui 2421 8月 13 16:46 spam.c
-rwxrwxr-x 1 hui hui 19560 8月 13 17:18 spam.cpython-37m-x86_64-linux-gnu.so
0
numpy.array数据交互
千万注意注册的模块名字保持一致,而且PyInit_${module_name}也要保持一致,否则会找不到。
处理numpy.array的cpp程序
// 官方建议:define PY_SSIZE_T_CLEAN before including Python.h
#define PY_SSIZE_T_CLEAN
// 由于Python.h中包含一些预处理,所以最好把这个include写在最前面
#include <Python.h>
#include <numpy/arrayobject.h>
#include <math.h>
static PyObject *SpamError;
// 使用PyArg_ParseTuple/PyArg_ParseTupleAndKeywords转换python传入的对象
// 使用PyLong_FromLong/Py_BuildValue转化为python对象传入python
static PyObject* cos_func_np(PyObject *self, PyObject *args)
{
// 注意在cpp里面调用的printf在调用时是打印不出来的
printf("hello world");
PyArrayObject *in_array;
if (!PyArg_ParseTuple(args, "O!", &PyArray_Type, &in_array)) {
PyErr_SetString(SpamError, "parse args fialed");
return NULL;
}
PyObject* out_array = PyArray_NewLikeArray(in_array, NPY_ANYORDER, NULL, 0);
if (out_array == NULL) {
PyErr_SetString(SpamError, "malloc memory for output failed");
return NULL;
}
PyArrayIterObject *in_iter = (PyArrayIterObject*)PyArray_IterNew((PyObject*)in_array);
PyArrayIterObject *out_iter = (PyArrayIterObject*)PyArray_IterNew((PyObject*)out_array);
if (in_iter == NULL || out_iter == NULL) {
PyErr_SetString(SpamError, "create iter failed.");
Py_XDECREF(out_array);
Py_XDECREF(in_iter);
Py_XDECREF(out_iter);
return NULL;
}
// while loop
while (in_iter->index < in_iter->size &&
out_iter->index < out_iter->size) {
double* in_dataptr = (double*)in_iter->dataptr;
double* out_dataptr = (double*)out_iter->dataptr;
*out_dataptr = cos(*in_dataptr);
PyArray_ITER_NEXT(in_iter);
PyArray_ITER_NEXT(out_iter);
}
Py_DECREF(in_iter);
Py_DECREF(out_iter);
// Py_DECREF(out_array); // 段错误 (核心已转储),由于out_array是要返回的,所以这里不能释放了
return out_array;
}
static PyMethodDef CosNumpyMethods[] = {
// METH_VARARGS. 一种标志,告诉解释器这将用于c函数的调用
// 如果传递的是可以通过 PyArg_ParseTuple处理的tuple(args)类型的python参数,则使用METH_VARARGS
// 如果传递的是可以通过PyArg_ParseTupleAndKeywords处理的kwargs类型的python参数,则使用METH_KEYWORDS
{"cos_func_np", cos_func_np, METH_VARARGS, "do cos for numpy.array."},
{NULL, NULL, 0, NULL}
};
static PyModuleDef CosNumpymodule = {
PyModuleDef_HEAD_INIT,
"CosNumpy", // module name
"CosNumpy: do cos for numpy.array", // doc, can be NULL
-1, // size of per-interpreter state of the module,
// or -1 if the module keeps state in global variables.
CosNumpyMethods
};
// 模块的初始化函数,必须名为PyInit_${module_name}
// PyMODINIT_FUNC: define PyMODINIT_FUNC extern "C" __declspec(dllexport) PyObject*
// 该函数在打开python时不会自动调用,如果想要创建自动调用的内建函数,
// 需使用PyImport_AppendInittab()添加到PyImport_Inittab表中
PyMODINIT_FUNC
PyInit_CosNumpy(void)
{
// 使用numpy.array 千万不要忘了这句
import_array();
PyObject *m;
m = PyModule_Create(&CosNumpymodule);
if (m == NULL)
return NULL;
SpamError = PyErr_NewException("CosNumpy.error", NULL, NULL);
Py_XINCREF(SpamError);
if (PyModule_AddObject(m, "error", SpamError) < 0) {
Py_XDECREF(SpamError);
Py_CLEAR(SpamError);
Py_DECREF(m);
return NULL;
}
return m;
}
setup.py
from distutils.core import setup, Extension
import numpy
module1 = Extension('CosNumpy',
# define_macros = [('MAJOR_VERSION', '1'), ('MINOR_VERSION', '0')],
include_dirs = [numpy.get_include()],
# libraries = [''],
# library_dirs = ['/usr/local/lib'],
sources = ['CosNumpy.c'])
setup (name = 'CosNumpy',
version = '1.0',
description = 'This is a cos_numpy package',
author = 'Martin v. Loewis',
author_email = 'martin@v.loewis.de',
url = 'https://docs.python.org/extending/building',
long_description = '''This is really just a cos_numpy package.''',
ext_modules = [module1])
编译运行
hui@hui-XPS-8920:~/github/BlogCode/python_invoke_cpp/python_C_API$ python setup_CosNumpy.py build_ext --inplace
running build_ext
building 'CosNumpy' extension
gcc -pthread -B /home/hui/ide/miniconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/hui/ide/miniconda3/lib/python3.7/site-packages/numpy/core/include -I/home/hui/ide/miniconda3/include/python3.7m -c CosNumpy.c -o build/temp.linux-x86_64-3.7/CosNumpy.o
In file included from /home/hui/ide/miniconda3/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1822:0,
from /home/hui/ide/miniconda3/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,
from /home/hui/ide/miniconda3/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,
from CosNumpy.c:5:
/home/hui/ide/miniconda3/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning"Using deprecated NumPy API, disable it with " "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp]
#warning "Using deprecated NumPy API, disable it with " \
^
gcc -pthread -shared -B /home/hui/ide/miniconda3/compiler_compat -L/home/hui/ide/miniconda3/lib -Wl,-rpath=/home/hui/ide/miniconda3/lib -Wl,--no-as-needed -Wl,--sysroot=/ build/temp.linux-x86_64-3.7/CosNumpy.o -o /home/data/github/BlogCode/python_invoke_cpp/python_C_API/CosNumpy.cpython-37m-x86_64-linux-gnu.so
hui@hui-XPS-8920:~/github/BlogCode/python_invoke_cpp/python_C_API$ ipython
Python 3.7.5 (default, Oct 25 2019, 15:51:11)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.10.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import CosNumpy
In [2]: import numpy as np
In [3]: x = np.arange(0, 2, 0.25) * np.pi
In [4]: CosNumpy.cos_func_np(x)
Out[4]:
array([ 1.00000000e+00, 7.07106781e-01, 6.12323400e-17, -7.07106781e-01,
-1.00000000e+00, -7.07106781e-01, -1.83697020e-16, 7.07106781e-01])
In [5]: exit()
hello world