前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C/C++与Python双剑合璧

C/C++与Python双剑合璧

作者头像
菜菜cc
发布2022-11-15 21:34:07
8230
发布2022-11-15 21:34:07
举报

????python作为一门动态语言,语法的灵活性和强大的模块支持使得开发效率大大提升,传统C/C++程序员可以借助python来实现业务逻辑来减少开发成本。而另一方面,python灵活的语言特性带来的代价是性能的降低,在一些密集计算型任务面前显得力不从心,但这个问题可以由C/C++来解决,将对性能要求较高的部分用C语言来实现即可, 而且对于一些加密解密算法,还可以保持源码的私密性。而本文正是针对两者的双剑合璧,对C/C++与python相互调用的讲解。

Python调用C模块

先实现C/C++版本的功能函数

笔者以斐波那契递归函数为例,将模块名定为cai,在python代码中用import cai导入该模块

首先笔者创建了一个cai.c和cai.h文件, 源码如下

cai.h

代码语言:javascript
复制
#ifndef __CAI_H__
#define __CAI_H__
                                                                                
#include <stdio.h>

int fib(int n);
int test(void);

#endif

cai.c

代码语言:javascript
复制
#include "cai.h"                                                                

// 斐波那契递归函数
int fib(int n)
{
    return n <= 2 ? 1 : fib(n - 1) + fib(n - 2); 
}

// 功能测试
//int main(void)
int test(void)
{
    for (int i = 1; i <= 10; i++)
        printf("fib[%d] = %d\n", i, fib(i));

    return 0;
}

编译测试,保证代码的正确性。

包裹C函数

实现包裹,主要分4步:

  1. 包含Python.h头文件/usr/include/python2.7/Python.h
  2. 为每一个函数增加一个PyObject *Module_func()的包裹函数
  3. 为模块增加一个形如PyMethod xxxMethods[]的数组 (xxx一般为模块名)
  4. 增加模块的初始化函数void initxxx() (xxx一般为模块名)

创建cai_wrapper.c文件,源码如下

cai_wrapper.c

代码语言:javascript
复制
#include "cai.h"
// 包含Python.h头文件 
#include <Python.h>

// 为fib函数增加包裹函数
static PyObject *cai_fib(PyObject *self, PyObject *args)
{
    // 用于存储从python传过来的参数
    int n;
    // 将python传递过来的int类型值转为C语言可识别的int类型值  
    // "i"表示python和C/C++之间int的转换,其他的转换可参见本程序代码后面的表格
    // PyArg_ParseTuple为可变参函数,如果需要包裹的函数为多个参数,参考示例: 
    // PyArg_ParseTuple(args,"lls", &k, &l, &s); /* Two longs and a string */
    if (!PyArg_ParseTuple(args, "i", &n)) {
        // 如果转化失败,则返回空
        return NULL;
    }

    // 将fib函数返回值的int类型转为python可识别的int类型
    return (PyObject *)Py_BuildValue("i", fib(n));
}

// 为test函数增加包裹函数
static PyObject* cai_test(PyObject *self, PyObject *args)
{
    test();
    return (PyObject *)Py_BuildValue("");
}

// 为模块增加一个形如PyMethod caiMethods[]的数组, 
// 每一个数组元素包含了在python中调用的函数名、对应的包裹函数名、METH_VARARGS常量,
// METH_VARARGS表示参数以tuple形式传递,
// 数组最后用两个NULL来表示函数信息列表的结束
static PyMethodDef caiMethods[] = {
    { "fib", cai_fib, METH_VARARGS },
    { "test", cai_test, METH_VARARGS },
    { NULL, NULL },
};

// 模块初始化
void initcai(void)
{
    // Py_InitModule函数的第一个参数为模块名, 第二个参数为上面定义的数组名
    Py_InitModule("cai", caiMethods);
}

Python与C/C++之间数据转化:

Format Code

Python Type

C/C++ Type

s

str

char *

z

str/None

char*/NULL

i

int

int

l

long

long

c

str

char

d

float

double

D

complex

Py_Complex *

O

(any)

PyObject *

S

str

PyStringObject

编译

创建setup.py文件,编译相关工作由setup.py来完成。

代码语言:javascript
复制
#coding=utf-8

from distutils.core import setup, Extension

# 模块名
MOD = 'cai'
# source参数为所有C/C++源代码的文件名列表
setup(name=MOD, ext_modules=[Extension(MOD, sources=['cai.c', 'cai_wrapper.c'])])

运行setup.py

代码语言:javascript
复制
python setup.py build

成功执行后,当前的目录结构为

会发现在build/lib.linux-x86_64-2.7下生成了cai.so动态链接库, 可以将模块安装到全局python模块路径下,使用如下命令

代码语言:javascript
复制
python setup.py install

或者进入到build/lib.linux-x86_64-2.7目录下来使用该模块

OK,!至此就完成了Python调用C/C++编写的模块!

C/C++调用Python

先实现python版本的功能函数

由于C++没有大数类,不支持大数乘法,而python先天的优势拥有大数算法,所以这里笔者以大数乘法为例,

创建了一个calc.py文件, 源码如下

calc.py

代码语言:javascript
复制
#coding=utf-8

def mul(a, b):
    val = long(a) * long(b)
    return str(val)

if __name__ == '__main__':
    print(mul('1234567890000', '9876543210000'))

在c++中调用python代码

利用python来实现大数乘法, 供c++使用,笔者创建了test.cpp,源码如下

test.cpp

代码语言:javascript
复制
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include "Python.h"

int main(void)
{
    // 初始化python
    Py_Initialize();

    // 初始化python系统环境路径
    // PyRun_SimpleString函数将传入的字符串直接当作python代码运行,成功返回0,失败返回-1
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./')");

    // 加载python文件  这里载入的是calc.py,但无需加后缀
    PyObject *pModule = PyImport_ImportModule("calc");

    // 加载函数   这里载入的是mul函数
    PyObject *pFunc = PyObject_GetAttrString(pModule, "mul");

    // 将参数压栈
    // 函数调用的参数传递均是以元组的形式打包的, 这里的2表示参数个数为2
    PyObject *pArgs = PyTuple_New(2);
    // 0表示元组的0下标,表示第一个参数,
    // Py_BuildValue用来将c/c++可识别的类型转为python可识别的,
    // Py_BuildValue的第一个参数为转换格式代码,第二个参数为传递给python函数的参数
    PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", "1234567890000"));
    // 第二个参数
    PyTuple_SetItem(pArgs, 1, Py_BuildValue("s", "9876543210000"));

    // 调用函数,并接收返回值
    PyObject *pReturn = PyEval_CallObject(pFunc, pArgs);

    // 将python返回来的参数类型转为c/c++可识别的类型
    char *val;
    PyArg_Parse(pReturn, "z", &val);

    // 关闭python
    Py_Finalize();

    std::cout << std::string(val) << std::endl;
}

执行以下命令生成可执行文件

代码语言:javascript
复制
g++ test.cpp -I/usr/include/python2.7  -lpython2.7 -o test.o

执行./test.o就完成了在c++中对python的调用

本文作者: Ifan Tsai ?(菜菜)

本文链接: /developer/article/2164598

版权声明: 本文采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-03-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Python调用C模块
    • 先实现C/C++版本的功能函数
      • 包裹C函数
        • 编译
        • C/C++调用Python
          • 先实现python版本的功能函数
            • 在c++中调用python代码
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
            http://www.vxiaotou.com