Python如何调用C和C++
admin
2024-01-17 20:27:44
0

ctypes库简介

根据Python官方文档,ctypes是一个外部函数库,它提供了与C兼容的数据类型,允许调用DLL(Dynamic Link Libraries, 动态链接库)或共享库中的函数。换句话说,通过ctypes库,我们能在Python程序中调用C/C++代码。

动态链接库是一个已编译的二进制文件,其在程序编译时并不会被链接到目标代码,而是在程序运行时才载入。Windows上的动态链接库为DLL(.dll),Linux上为SO(.so

示例演示

先给一个演示的Demo,然后再展开来讲,演示环境说明如下:

Platform: Ubuntu-20.04
gcc: 9.4.0

首先新建一个名为test.cpp的源码文件,源码内容如下:

extern "C"{void greet(char* name){std::cout << "Hello " << name << std::endl;}int sumArray(int a [], int n){int s = 0;for (int i = 0; i < n;i++){s += a[i];}return s;}double distance(double *x, double y[], int n){double dis;for (int i = 0; i < n;i++){dis += pow((x[i] - y[i]), 2);}return sqrt(dis);}
}

然后通过下述命令将其编译为动态链接库:

g++ -fPIC -shared test.cpp -o test.so

Python调用上述三个函数的示例如下:

from ctypes import *# 加载
lib = CDLL("./test.so")lib.greet(b"Tom")
# Hello Tomint_5 = c_int * 5
arr = int_5(1, 3, 5, 7, 9)
print(lib.sumArray(arr, 5))
# 25double_2 = c_double * 2
x = double_2(1, 3)
y = double_2(2, 4)
distance = lib.distance
distance.restype = c_double
print(distance(x, y, 2))
# 1.4142135623730951

C/C++部分

从上述演示示例可以看出,对于C/C++源码仅需要将其编译为动态链接库即可,上述的示例命令为:

g++ -fPIC -shared test.cpp -o test.so

说明:

  • 若为C程序使用gcc,若为C++程序则使用g++
  • -fPIC表示位置独立。
  • -shared表示编译为动态库。

另外,需要注意在上述示例代码中还使用了extern "C" {}的用法,其作用是告知编译器按C的方式编译,编译后可以直接通过函数名调用。这在编译C++程序时是必须的,因为C++支持函数重载,导致编译后函数名会发生改变,使得不能通过函数名来对C++程序中的函数进行调用。

注意:extern修饰代表本模块可以在外部使用,若不想暴露相应的接口,则可以使用static修饰。

Python部分

加载动态库

在Python中载入动态链接库的方式如下所示:

from ctypes import *# 方式1
lib = CDLL("./test.so")# 方式2
lib = cdll.LoadLibrary("./test.so")

载入完成后便可以通过.来调用C/C++程序中的内容。

数据类型间的对应关系

ctypes中定义的与C兼容的基本数据类型完整版详见官网Fundamental data types。本文仅列举一些常用的:

ctypes类型C类型Python类型
c_bool_Bool布尔型
c_charchar单字符字节对象
c_intint整型
c_floatfloat浮点型
c_doubledouble浮点型(python不区分单精度还是双精度)

字符串

Python中通过ctypes传递参数类型为字符串的形式如下:

# 形式一
lib.travel(b"I love Python!")# 形式二:使用字符指针
m_str = c_char_p(b"I love Python!")
lib.travel(m_str)# 形式三
m_str = "我爱Python!"
lib.travel(m_str.encode())# 形式四:创建String Buffer
m_str = create_string_buffer(("我爱Python!").encode())
lib.travel(m_str)

其中traval是C++中遍历打印字符串的一个函数,函数定义如下:

void travel(char * str){for(int i = 0;str[i];i++){std::cout << str[i];}std::cout << std::endl;
}

说明:

  • 加上b可以让字符串强制转为bytes类型,但这种方式仅限于只包含ASCII字符的字符串。
  • 若字符串中不仅包含ASCII字符,可以使用encoode()方法。
  • ctypes提供了函数create_string_buffer()来创建字符串缓冲区,其属于可修改的字符串传参方式

下面的示例便验证了create_string_buffer能创建可修改的字符串参数。

C++中修改函数定义:

void increment(char * str){for(int i = 0; str[i]; i++){str[i] += 1;}
}

Python中调用及结果如下:

m_str = create_string_buffer(b"abc")
lib.increment(m_str)
print(m_str.value)
# b'bcd'

指针/引用

指针可以通过ctypes中的pointer(obj)函数进行创建:

pi = c_float(3.14)
ptr = pointer(pi)
print(ptr.contents)
# c_float(3.140000104904175)

引用可以通过ctypes中的byref(obj)函数进行创建:

pi = c_float(3.14)
ptr = byref(pi)

注意:官网指明若只想向外部函数传递一个对象指针,使用引用更快

下面给出一个交换两个元素值的例子:

C++部分源码为:

void swap(int *a, int &b){int t = *a;*a = b;b = t;
}

Python部分源码为:

x = c_int(3)
y = c_int(4)
lib.swap(pointer(x), byref(y))
print(x.value, y.value)
# 4 3

数组类型

创建数组类型的推荐方式是类型乘以一个正数,例如:

int_3 = c_int * 3

传参

通过argtypes属性可以指定函数的传参类型。示例如下:

print_str = lib.travel
print_str.argtypes = [c_char_p]

返回值类型

默认情况下都假定函数返回c_int类型,但可以通过函数对象的restype属性可以指定返回值的类型。在上述示例演示中便有一个现成的例子,其指定了返回值类型是c_double

distance.restype = c_double

参考资料

完成本文参考了如下资料:

  • ctypes — Python 的外部函数库
  • 浅谈python中使用C/C++:ctypes
  • Python ctypes Tutorial

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...