1.最基本的调用
1.生成so库
-
创建c++头文件和实现
头文件
#ifndef GENASO_UTILS_H #define GENASO_UTILS_H #include <iostream> using namespace std; void sayHello(); #endif //GENASO_UTILS_H
实现
#include "utils.h" void sayHello(){ cout<<"hello world"<<endl; }
CMakeLists.txt文件
cmake_minimum_required(VERSION 3.14) project(GenaSo) set(CMAKE_CXX_STANDARD 14) add_library(utils SHARED utils.cpp utils.h)
编译项目生成utils动态库
2.C++工程使用
拷贝libutils.so
和utils.h
到工程下
配置CMakeLists.txt
文件
cmake_minimum_required(VERSION 3.14)
project(TestSo)
set(CMAKE_CXX_STANDARD 14)
# 设置头文件的路径
set(INCLUDE_DIR ./3rdparty/include)
set(LIB_DIR ./3rdparty/lib)
# 头文件和链接库的路径
include_directories(${INCLUDE_DIR})
link_directories(${LIB_DIR})
add_executable(TestSo main.cpp)
target_link_libraries(
TestSo
utils
)
3.python使用c++库
python中使用c++的so库需要用到ctypes
这个库
ctypes是Python的一个外部库,提供和C语言兼容的数据类型,可以很方便地调用DLL和so库中输出的C接口函数
注意:
ctypes是调用c语言的库,c++的库不能直接调用头文件需要处理如下
-
处理头文件
#ifndef GENASO_UTILS_H #define GENASO_UTILS_H #include <iostream> using namespace std; # 以c语言的语法解析 extern "C" { void sayHello(); }; #endif //GENASO_UTILS_H
再次生成so库
-
python中调用
from ctypes import * # 加载so库 cur = cdll.LoadLibrary('./libutils.so') # 调用so库中的函数 cur.sayHello()
2.函数参数为普通数据类型参数
头文件
extern "C" {
// 普通函数
void sayHello();
// 普通参数函数
int add(int a,int b);
};
实现
// 普通参数函数
int add(int a,int b){
return a+b;
}
python中使用
from ctypes import *
# 加载so库
cur = cdll.LoadLibrary('./libutils.so')
# 调用add函数
sum = cur.add(10,30)
print(sum)
3.double类型
除了整型、字节以及字符串外,其它数据类型都需要通过ctypes进行包装
-
定义c++的头文件和实现并且打包库
头文件
#ifndef GENASO_UTILS_H #define GENASO_UTILS_H #include <iostream> using namespace std; extern "C" { //传递double类型 double addDouble(double a, double b); }; #endif //GENASO_UTILS_H
实现
#include "utils.h" //传递double类型 double addDouble(double a, double b){ return a+b; }
-
python中调用
调用方式一:
from ctypes import * # 加载so库 cur = cdll.LoadLibrary('./libutils.so') # 告诉解释器参数类型 cur.addDouble.argtypes = [c_double,c_double] # 告诉解释器返回值类型 cur.addDouble.restype = c_double # 调用函数 result = cur.addDouble(1.123,2.123) print(result)
调用方式二(不指定参数类型):
from ctypes import * # 加载so库 cur = cdll.LoadLibrary('./libutils.so') # 告诉解释器返回值类型 cur.addDouble.restype = c_double # 调用函数 result = cur.addDouble(c_double(1.123),c_double(2.123)) print(result)
4. python和c语言数据类型对比
ctypes类型 | C型 | Python类型 |
---|---|---|
c_bool |
_Bool |
布尔(1) |
c_char |
char |
1个字符的字节对象 |
c_wchar |
wchar_t |
1个字符的字符串 |
c_byte |
char |
INT |
c_ubyte |
unsigned char |
INT |
c_short |
short |
INT |
c_ushort |
unsigned short |
INT |
c_int |
int |
INT |
c_uint |
unsigned int |
INT |
c_long |
long |
INT |
c_ulong |
unsigned long |
INT |
c_longlong |
__int64 要么 long long
|
INT |
c_ulonglong |
unsigned __int64 要么 unsigned long long
|
INT |
c_size_t |
size_t |
INT |
c_ssize_t |
ssize_t 要么 Py_ssize_t
|
INT |
c_float |
float |
浮动 |
c_double |
double |
浮动 |
c_longdouble |
long double |
浮动 |
c_char_p |
char * (NUL终止) |
bytes对象或 None
|
c_wchar_p |
wchar_t * (NUL终止) |
字符串或 None
|
c_void_p |
void * |
int或 None
|
5.函数进出参数定义
-
argtypes
python不会直接读取到库文件,需要通过这个参数告诉python解释器函数参数类型
-
restype
告诉python解释器函数的返回值类型
6.数组参数
-
定义头文件和实现以及打包成库
#ifndef GENASO_UTILS_H #define GENASO_UTILS_H #include <iostream> using namespace std; extern "C" { //传递数组参数 int transArr(int arr[],int length); };
-
python中调用
from ctypes import * # 加载so库 cur = cdll.LoadLibrary('./libutils.so') # python列表 l = [1,2,3,4] # 转换列表 newL = (c_int *len(l))(*l) # 调用函数 cur.transArr(newL,len(l))
注意:
ctypes重载了,因此我们可以用类型n来表示n个该类型的元素在一起组成一个整体。如定义整数数组类型:
int_10 = c_int *10
上面newL = (c_int *len(l))(*l)
第一个*
代表数组
第二个*
代表对列表解包
7.字符串参数
-
定义c++
头文件
#ifndef GENASO_UTILS_H #define GENASO_UTILS_H #include <iostream> using namespace std; extern "C" { //传递字符串 void transStr(char *str); }; #endif //GENASO_UTILS_H
实现
#include "utils.h" //传递字符串 void transStr(char *str){ cout<<str<<endl; }
-
python调用
from ctypes import * # 加载so库 cur = cdll.LoadLibrary('./libutils.so') # cur.transStr.argtypes = [c_char_p] cur.transStr("你好".encode())
8.传递结构体类型参数
-
c++实现
头文件
#ifndef GENASO_UTILS_H #define GENASO_UTILS_H #include <iostream> using namespace std; extern "C" { //定义结构体类型 struct Student { char name[20]; int age; }; //传递结构体 void transStudent(struct Student stu); }; #endif //GENASO_UTILS_H
实现
#include "utils.h" //传递结构体 void transStudent(struct Student stu){ cout<<stu.name<<"---"<<stu.age<<endl; }
-
python调用
from ctypes import * # 加载so库 cur = cdll.LoadLibrary('./libutils.so') # 必须要集成Structure class Student(Structure): # 指定结构体字段 _fields_ = [("name", c_char* 20), ("age", c_int)] stu = Student() # 字节数组 stu.name = b"bill" stu.age = 30 # 传递结构体 cur.transStudent(stu)
注意:
如果结构体中name
的类型为cahr *
则python中定义的时候name属性为:
class Student(Structure):
# 指定结构体字段
_fields_ = [("name", c_char_p),
("age", c_int)]
9.传递指针
-
c++实现
头文件
#ifndef GENASO_UTILS_H #define GENASO_UTILS_H #include <iostream> using namespace std; extern "C" { //传递指针 int addPointer(int *pa,int *pb); }; #endif //GENASO_UTILS_H
实现
#include "utils.h" //传递指针 int addPointer(int *pa,int *pb){ return *pa+*pb; }
-
python中使用
from ctypes import * # 加载so库 cur = cdll.LoadLibrary('./libutils.so') a = c_int(10) b = c_int(20) # 传递指针 result = cur.addPointer(pointer(a),pointer(b)) print(result)
10.传递引用
-
c++实现
头文件
#ifndef GENASO_UTILS_H #define GENASO_UTILS_H #include <iostream> using namespace std; extern "C" { //传递引用 int addRef(int &a,int &b); }; #endif //GENASO_UTILS_H
实现
#include "utils.h" //传递引用 int addRef(int &a,int &b){ return a+b; }
-
python中使用
from ctypes import * # 加载so库 cur = cdll.LoadLibrary('./libutils.so') a = c_int(10) b = c_int(20) # 传递引用数据 result = cur.addRef(byref(a),byref(b)) print(result)
11.python调用c语言的回调函数
-
c++实现
头文件
#ifndef GENASO_UTILS_H #define GENASO_UTILS_H #include <iostream> using namespace std; extern "C" { //回调函数 void regist(int age,void (*callback)(bool)); }; #endif //GENASO_UTILS_H
实现
#include "utils.h" //回调函数 void regist(int age,void (*callback)(bool)){ if(age>50){ callback(false); } else{ callback(true); } }
-
python中实现
from ctypes import * # 加载so库 cur = cdll.LoadLibrary('./libutils.so') # 回调函数 def callBack(result): print(result) # 定义回调函数的类型 CALLBACKFUNC = CFUNCTYPE(None,c_bool) # 传递回调函数 cur.regist(10,CALLBACKFUNC(callBack))