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函数(无第三方库调用)

调用按照数据交互分成以下几种

  1. 无参数,无输入的函数 do_nothing()
  2. 基础类型参数,单返回值 c=add(a, b)
  3. 基础类型参数,多返回值 c,d = add_sub(a, b)
  4. 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