ctypes使用 混合编程

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

2.C++工程使用

拷贝libutils.soutils.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++的库不能直接调用头文件需要处理如下

  1. 处理头文件

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

    再次生成so库

  2. 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;
    }
    
  2. 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);
    };
    
  2. 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;
    }
    
  2. 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;
    }
    
  2. 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;
    }
    
  2. 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))
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容