使用ctypes在Python中调用C++动态库
使用ctypes在Python中调用C++动态库
入门操作
使用ctypes库可以直接调用C语言编写的动态库,而如果是调用C++编写的动态库,需要使用extern关键字对动态库的函数进行声明:
#include <iostream>
using namespace std;
extern "C" {
void greet() {
cout << "hello python" << endl;
}
}
将上述的C++程序编译成动态链接库:
g++ hello.cpp -fPIC -shared -o hello.so
在Python代码中使用ctypes导入动态库,调用函数:
# -*- coding: utf-8 -*- #
from ctypes import CDLL
hello = CDLL('./hello.so')
if __name__ == '__main__':
hello.greet()
运行上述Python程序:
xujijun@pc:~/codespace/python$ python3 hello.py
hello python
参数传递
编写一个整数加法函数
#include <iostream>
using namespace std;
extern "C" {
int add(int a, int b) {
return a + b;
}
}
编译得到动态库,在Python代码中调用:
# -*- coding: utf-8 -*- #
from ctypes import CDLL
hello = CDLL('./hello.so')
if __name__ == '__main__':
a = input('input num1: ')
b = input('input num2: ')
print('output: %d' % hello.add(int(a), int(b)))
运行上述代码,得到输出:
xujijun@pc:~/codespace/python$ python3 hello.py
input num1: 12
input num2: 34
output: 46
尝试传递字符串参数
#include <iostream>
#include <cstdio>
using namespace std;
extern "C" {
void print_name(const char* name) {
printf("%s\n", name);
}
}
Python代码调用:
# -*- coding: utf-8 -*- #
from ctypes import CDLL
hello = CDLL('./hello.so')
if __name__ == '__main__':
name = input('input name: ')
hello.print_name(name.encode('utf-8')) # 此处需要将Python中的字符串按照utf8编码成bytes
查看输出:
xujijun@pc:~/codespace/python$ python3 hello.py
input name: yanhewu
yanhewu
面向对象
使用C++编写动态链接库我们肯定会想到如何在Python中调用C++的类,由于ctypes只能调用C语言函数(C++中采用extern "C"声明的函数),我们需要对接口进行一定的处理:
C++代码示例
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
class Student {
private:
string name;
public:
Student(const char* n);
void PrintName();
};
Student::Student(const char* n) {
this->name.assign(n);
}
void Student::PrintName() {
cout << "My name is " << this->name << endl;
}
extern "C" {
Student* new_student(const char* name) {
return new Student(name);
}
void print_student_name(Student* stu) {
stu->PrintName();
}
}
Python代码中调用:
# -*- coding: utf-8 -*- #
from ctypes import CDLL
hello = CDLL('./hello.so')
class Student(object):
def __init__(self, name):
self.stu = hello.new_student(name.encode('utf-8'))
def print_name(self):
hello.print_student_name(self.stu)
if __name__ == '__main__':
name = input('input student name: ')
s = Student(name)
s.print_name()
输出:
xujijun@pc:~/codespace/python$ python3 hello.py
input student name: yanhewu
My name is yanhewu
内存泄漏?
上一部分我们我们尝试了如何使用ctypes调用带有类的C++动态库,这里我们不禁会想到一个问题,我们在动态库中使用new动态申请的内存是否会被Python的GC清理呢?这里我们完全可以猜想,C++动态库中的动态内存并不是使用Python中的内存申请机制申请的,Python不应该对这部分内存进行GC,如果真的是这样,C++的动态库就会出现内存泄漏的问题了。那事实是不是这样呢?我们可以使用内存检查工具Valgrind来检查上面的Python代码:
命令:
valgrind python3 hello.py
最终结果输出:
==17940== HEAP SUMMARY:
==17940== in use at exit: 647,194 bytes in 631 blocks
==17940== total heap usage: 8,914 allocs, 8,283 frees, 5,319,963 bytes allocated
==17940==
==17940== LEAK SUMMARY:
==17940== definitely lost: 32 bytes in 1 blocks
==17940== indirectly lost: 0 bytes in 0 blocks
==17940== possibly lost: 4,008 bytes in 7 blocks
==17940== still reachable: 643,154 bytes in 623 blocks
==17940== suppressed: 0 bytes in 0 blocks
==17940== Rerun with --leak-check=full to see details of leaked memory
==17940==
==17940== For counts of detected and suppressed errors, rerun with: -v
==17940== Use --track-origins=yes to see where uninitialised values come from
==17940== ERROR SUMMARY: 795 errors from 86 contexts (suppressed: 0 from 0)
可以看到,definitely lost了32字节,确实出现了内存泄漏,但是是不是动态库的问题我们还要进一步验证:
C++代码加入析构函数定义:
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
class Student {
private:
string name;
public:
Student(const char* n);
~Student();
void PrintName();
};
Student::Student(const char* n) {
this->name.assign(n);
}
Student::~Student() {
cout << "Student's destructor called" << endl;
}
void Student::PrintName() {
cout << "My name is " << this->name << endl;
}
extern "C" {
Student* new_student(const char* name) {
return new Student(name);
}
void print_student_name(Student* stu) {
stu->PrintName();
}
}
运行相同的Python代码:
xujijun@pc:~/codespace/python$ python3 hello.py
input student name: yanhewu
My name is yanhewu
从输出可以看到,Student的析构函数并没有被调用。这里可以确定,Python的GC并没有对动态库中申请的内存进行处理,也确实不能进行处理(毕竟不是Python环境下申请的内存,在C++动态库中可能会先释放这部分内存,如果GC再次释放就会出现内存问题)。但是内存泄漏的问题还是需要解决的,可以参照以下做法:
C++代码中添加内存释放接口:
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
class Student {
private:
string name;
public:
Student(const char* n);
~Student();
void PrintName();
};
Student::Student(const char* n) {
this->name.assign(n);
}
Student::~Student() {
cout << "Student's destructor called" << endl;
}
void Student::PrintName() {
cout << "My name is " << this->name << endl;
}
extern "C" {
Student* new_student(const char* name) {
return new Student(name);
}
// 释放对象内存函数
void del_student(Student* stu) {
delete stu;
}
void print_student_name(Student* stu) {
stu->PrintName();
}
}
Python代码中,在Student类中调用内存释放函数:
# -*- coding: utf-8 -*- #
from ctypes import CDLL
hello = CDLL('./hello.so')
class Student(object):
def __init__(self, name):
self.stu = hello.new_student(name.encode('utf-8'))
def __del__(self):
# Python的对象在被GC时调用__del__函数
hello.del_student(self.stu)
def print_name(self):
hello.print_student_name(self.stu)
if __name__ == '__main__':
name = input('input student name: ')
s = Student(name)
s.print_name()
运行Python代码:
xujijun@pc:~/codespace/python$ python3 hello.py
input student name: yanhewu
My name is yanhewu
Student's destructor called
可以看到,C++动态库中的Student类的析构函数被调用了。再次使用Valgrind检查内存使用情况:
==23780== HEAP SUMMARY:
==23780== in use at exit: 647,162 bytes in 630 blocks
==23780== total heap usage: 8,910 allocs, 8,280 frees, 5,317,023 bytes allocated
==23780==
==23780== LEAK SUMMARY:
==23780== definitely lost: 0 bytes in 0 blocks
==23780== indirectly lost: 0 bytes in 0 blocks
==23780== possibly lost: 4,008 bytes in 7 blocks
==23780== still reachable: 643,154 bytes in 623 blocks
==23780== suppressed: 0 bytes in 0 blocks
==23780== Rerun with --leak-check=full to see details of leaked memory
==23780==
==23780== For counts of detected and suppressed errors, rerun with: -v
==23780== Use --track-origins=yes to see where uninitialised values come from
==23780== ERROR SUMMARY: 793 errors from 87 contexts (suppressed: 0 from 0)
可以看到,definitely lost已经变为0,可以确定动态库申请的内存被成功释放。
使用ctypes在Python中调用C++动态库的更多相关文章
- JNI_Android项目中调用.so动态库
JNI_Android项目中调用.so动态库 2014年6月3日 JNI学习 參考:http://blog.sina.com.cn/s/blog_4298002e01013zk8.html 上一篇笔者 ...
- CVI中调用VC动态库
1.在VC环境中建立新工程,创建32位动态库(Win32 Dynamic-Link Library) -> A simple DLL project 2.在工程中可加入别的动态库,在工程菜单中 ...
- JNI_Android项目中调用.so动态库实现详解
转自:http://www.yxkfw.com/?p=7223 1. 在Eclipse中创建项目:TestJNI 2. 新创建一个class:TestJNI.java package com.wwj. ...
- JNI_Android项目中调用.so动态库实现详解【转】
转自 http://www.cnblogs.com/sevenyuan/p/4202759.html 1. 在Eclipse中创建项目:TestJNI 2. 新创建一个class:TestJNI.ja ...
- JNI_Android 项目中调用.so动态库实现详解
转自:http://www.yxkfw.com/?p=7223 1. 在Eclipse中创建项目:TestJNI 2. 新创建一个class:TestJNI.java package com.wwj. ...
- golang调用c动态库
golang调用c动态库 简介 golang调用c语言动态库,动态方式调用,可指定动态库路径,无需系统目录下 核心技术点 封装c动态库 go语言调用c代码 实例代码 封装c动态库 头文件 test_s ...
- 如何在python中调用C语言代码
1.使用C扩展CPython还为开发者实现了一个有趣的特性,使用Python可以轻松调用C代码 开发者有三种方法可以在自己的Python代码中来调用C编写的函数-ctypes,SWIG,Python/ ...
- python调用.net动态库
# python调用.net动态库 ### pythonnet简介------------------------------ pythonnet是cpython的扩展- pythonnet提供了cp ...
- Python脚本传參和Python中调用mysqldump
Python脚本传參和Python中调用mysqldump<pre name="code" class="python">#coding=utf-8 ...
随机推荐
- dbgrid多选日记
procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char); begin then begin DBGrid1.DataSourc ...
- 【bzoj3518】点组计数 欧拉函数(欧拉反演)
题目描述 平面上摆放着一个n*m的点阵(下图所示是一个3*4的点阵).Curimit想知道有多少三点组(a,b,c)满足以a,b,c三点共线.这里a,b,c是不同的3个点,其顺序无关紧要.(即(a,b ...
- Django 2.0 学习(18):Django 缓存、信号和extra
Django 缓存.信号和extra Django 缓存 由于Django是动态网站,所以每次请求均会去数据库进行相应的操作,当程序访问量大时,耗时必然会显著增加.最简单的解决方法是:使用缓存,缓存将 ...
- BZOJ3714 PA2014Kuglarz(最小生成树)
每次询问所获得的可以看做是两个前缀和的异或.我们只要知道任意前缀和的异或就可以得到答案了.并且显然地,如果知道了a和b的异或及a和c的异或,也就知道了b和c的异或.所以一次询问可以看做是在两点间连边, ...
- vue使用过程中的一些小技巧
这些也是自己平时项目中遇到过的一些问题,看到有人整理了出来,也就转载保存一下 文章内容总结: 组件style的scoped Vue 数组/对象更新 视图不更新 vue filters 过滤器的使用 列 ...
- Frequent values UVA - 11235(巧妙地RMQ)
题意: 给出一个非降序排列的整数数组a1.a2,······,an,你的任务是对于一系列询问(i,j),回答ai,ai+1,······,aj中出现次数最多的值所出现的次数 解析: 白书p198 其实 ...
- 【BZOJ4767】两双手(动态规划,容斥)
[BZOJ4767]两双手(动态规划,容斥) 题面 BZOJ 题解 发现走法只有两种,并且两维坐标都要走到对应的位置去. 显然对于每个确定的点,最多只有一种固定的跳跃次数能够到达这个点. 首先对于每个 ...
- BZOJ5314 [Jsoi2018]潜入行动 【背包类树形dp】
题目链接 BZOJ5314 题解 设\(f[i][j][0|1][0|1]\)表示\(i\)为根的子树,用了\(j\)个监测器,\(i\)节点是否被控制,\(i\)节点是否放置的方案数 然后转移即可 ...
- 网络协议之DHCP与Route20170330
由于要使用网络通讯,所以不可避免的要用到dhcp.理想的网络通讯方式是下面3种都要支持: 1,接入已有网络.这便要求可以作为dhcp客户端. 2,作为DHCP服务器,动态分配IP. 3,指定固定IP ...
- Lua弱表Weak table
定义:弱表的使用就是使用弱引用,很多程度上是对内存的控制. 1.weak表示一个表,它拥有metatable,并且metatable定义了__mode字段. 2.弱引用不会导致对象的引用计数变化.换言 ...