1.最基本的调用

1.生成so库

  1. 创建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)
  1. 编译项目生成utils动态库

2.C++工程使用

拷贝libutils.soutils.h到工程下

配置CMakeLists.txt文件

 

cmake_minimum_required(VERSION 3.14)
project(TestSo)

set(CMAKE_CXX_STANDARD 14)

# 设置头文件的路径
set(INCLUDE_DIR ./test/include)
set(LIB_DIR ./test/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++的库不能直接调用头文件需要处理如下

  1. 处理头文件
#ifndef GENASO_UTILS_H
#define GENASO_UTILS_H

#include <iostream>
using namespace std;
# 以c语言的语法解析
extern "C" {
void sayHello();
};

#endif //GENASO_UTILS_H

再次生成so库

  1. 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进行包装

  1. 定义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;
}
  1. 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.数组参数

  1. 定义头文件和实现以及打包成库
#ifndef GENASO_UTILS_H
#define GENASO_UTILS_H

#include <iostream>
using namespace std;

extern "C" {
//传递数组参数
int transArr(int arr[],int length);
};
  1. 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.字符串参数

  1. 定义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;
}
  1. python调用
from ctypes import *
# 加载so库
cur = cdll.LoadLibrary('./libutils.so')

# cur.transStr.argtypes = [c_char_p]
cur.transStr("你好".encode())

8.传递结构体类型参数

  1. 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;
}
  1. 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.传递指针

  1. 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;
}
  1. 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.传递引用

  1. 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;
}
  1. 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语言的回调函数

  1. 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);
    }
}
  1. 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))