Python调用C++代码用法——Linux

news2024/10/5 17:38:26

目录

前言

C/C++动态共享库编译

ctype模块

ctype数据类型

使用案例

float数据

指针

 结构体及结构体指针

numpy图像当作指针传入

参考资料:


前言

        在项目开发中,有时会使用到多种编程语言,比如部分功能是C/C++代码实现的,而另一部分是Python代码实现的,这样就可能需要使用多种编程语言。当然,也可以把C/C++代码转成Python,但这样可能费时;也有可能某个模块用Python来实现,速度很慢,但用C/C++速度却很快,这就会使用到用Python来调用C/C++代码。

        本文主要说明如何使用Python来调用C/C++代码,且是基于Linux平台,Linux上编译生成动态共享库so文件,然后Python使用ctype模块来调用so文件里的函数。如果不同平台,比如Windows或者mac,编译的动态共享库文件是不一样的。

C/C++动态共享库编译

        首先要把C/C++代码编译成动态共享库,一般是通过extern C来实现对外接口。下面给出一个例子。test.h文件进行函数声明,test.cpp是函数实现。

/******c动态库接口文件test.h*************/
#ifndef TEST_H
#define TEST_H

#ifdef __cplusplus
extern "C" {
#endif

    int hello();

#ifdef __cplusplus
}
#endif

#endif /* TEST_H */
/*****c动态库函数实现test.cpp*****************/
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include "test.h"

int hello()
{
    printf("hello world\n");
    return 0;
}

编译命令为:g++ -std=c++11 -o libtest.so -shared -fPIC test.cpp。编译完成后,就会在当前路径下生成libtest.so文件。

ctype模块

        通过ctype模块,可以加载动态库到进程中并返回其实例对象,该方法每次都会返回一个新实例,使用方法如下:

import ctypes

mylib = ctypes.cdll.LoadLibrary("libtest.so") #此次可能要加入绝对路径

ctype数据类型

        因为ctypes只能调用C编译成的库,因此不支持重载,需要在程序中显示定义函数类型和返回值类型。ctypes基本数据类型如下:

        在定义函数的参数和返回值时,必须使用ctypes的数据类型,若没有显示定义参数类型和返回类型,python默认为int型。参数类型用关键字argtypes定义,返回类型用restype定义,其中argtypes必须是一个序列,如tuple或者list,否则会报错,好像不使用参数类型关键字和返回类型也行。

使用案例

float数据

C/C++代码

float addfloat(float a, float b)
{
    printf("a = %f, b = %f \n", a, b);
    return a + b;
}

Python端代码

def c_basic_test():
    a = ctype.c_float(2.1)
    b = ctype.c_float(3.5)
    c = mylib.addfloat(a, b)
    print(c)

指针

        从上面的表格中,我们可以看到char*和void*已经有专用类型了,直接使用即可,对于其他类型的指针,ctypes提供了两种定义方式pointer和POINTER。POINTER必须传入ctypes类型,创建出新的ctypes指针类型(pointer type),而pointer传入一个对象,创建出一个新的指针实例。(POINTER创建出了pointer)。

        传输地址,ctypes提供了byref函数,ctypes.byref(obj[, offset]),该函数返回一个指向ctypes实例对象的轻量级指针,函数中还可以通过参数(必须是int)来设置偏移地址,这个返回的对象只能用于外部函数调用的参数。

 C/C++代码

void pointerTest(int* pInt, float* pFloat)
{
    *pInt = 10;
    *pFloat = 12.34;
}

Python代码

def c_pointer_test():
    # 好像可有可无
    #library.pointerTest.argtypes = [POINTER(c_int), POINTER(c_float)]
    #library.pointerTest.restype = c_void_p
    a = 0
    b = 0
    int_a = ctype.c_int(a)
    float_b = ctype.c_float(b)

    # byref()函数用于传输地址
    mylib.pointerTest(byref(int_a), byref(float_b))
    print("out_a:", int_a.value)
    print("out_b", float_b.value)

 外部传输字符空间,在函数内部进行赋值

C/C++代码

void mallocTest(char* pszStr)
{
    strcpy(pszStr, "Happay Children's day");
}

Python代码

def c_malloc_test():
    library.mallocTest.argtypes = [c_char_p]
    library.mallocTest.restype = c_void_p

    word = (c_char * 32)()
    mylib.mallocTest(word)
    print("out_word:", word.value)

 结构体及结构体指针

        结构体是C/C++中常用类型,使用前要先定义其成员类型,在python中也是同样处理,python中的结构体必须继承Structure类,定义其成员必须使用_field_属性。该属性是一个list,其成员都是2个值的tuple,分别是每个结构体成员的类型和长度,而且定义类型必须使用ctypes类型或者由ctype组合而成的新类型。而且结构体还存在嵌套的情况,这里也嵌套的结构体为例。

 C/C++代码

struct sub_struct{
    char* test_char;
    int test_int;
};

struct struct_def {
    char* stru_string;
    int stru_int;
    char stru_arr_num[4];
    sub_struct son_struct;
};


int test(struct_def struct_mystruct, struct_def* struct_test_p) 
{
    //输出结构体指针的数据
    cout<<"输出结构体中的char*字符:";
    cout << struct_mystruct.stru_string << endl;
    cout<<"输出结构体中的int型:";
    cout << struct_mystruct.stru_int <<endl;
    cout <<"输出结构体中的字符数组:";
    for(int x = 0;x< 4;x++) {
        cout << struct_mystruct.stru_arr_num[x]<<"   ";
    }
    cout<< endl;
    cout<<"输出子结构体中的char*型:";
    cout << struct_mystruct.son_struct.test_char<<endl;
    cout<<"输出子结构体中的int型:";
    cout<<struct_mystruct.son_struct.test_int<<endl;
    //输出结构体指针的数据
    cout<<endl;
    cout<<endl;
    cout<<"输出结构体指针中的char*字符:";
    cout << struct_test_p->stru_string << endl;
    cout<<"输出结构体指针中的int型:";
    cout << struct_test_p->stru_int <<endl;
    cout <<"输出结构体指针中的字符数组:";
    for(int x = 0;x< 4;x++) {
        cout << struct_test_p->stru_arr_num[x]<<"   ";
    }
    cout<< endl;
    cout<<"输出子结构体指针中的字符串:";
    cout<<struct_test_p->son_struct.test_char;
    cout << endl;
    cout<<"输出子结构体指针中的int型:";
    cout<<struct_test_p->son_struct.test_int<<endl;
}

Python代码

class sub_struct(ctypes.Structure):
    _fields_ = [
        ("test_char_p",ctypes.c_char_p),
        ("test_int",ctypes.c_int)
    ]

class struct_def(ctypes.Structure):
    _fields_ = [
        ("stru_string",ctypes.c_char_p),
        ("stru_int", ctypes.c_int),
        ("stru_arr_num", ctypes.c_char*4),
        ("son_struct", sub_struct)
    ]

struct_mystruct = struct_def()
struct_mystruct.stru_string = b"string in the struct"
struct_mystruct.stru_int = 99
struct_mystruct.stru_arr_num = b"ABCD"
struct_mystruct.son_struct.test_char_p =b"sub struct of the string"
struct_mystruct.son_struct.test_int = 10
mylib.test(struct_mystruct,ctypes.byref(struct_mystruct))

numpy图像当作指针传入

        在处理图像时,经常是传入一张图像,输出另外一张图像,图像处理部分是C/C++代码处理,这里也把彩色图像转出灰度图像为例。

C/C++代码

void GetGrayImage(unsigned char *rgb, unsigned char *gray, int width, int height)
{
    for(int j =0; j < height; j++) {
        for(int i = 0; i < width; i++) {
            int r = rgb[j * width + i];
            int g = rgb[j * width + i + width * height];
            int b = rgb[j * width + i + 2 * width * height];
            int val = 0.299 * r + 0.587 * g + 0.114 * b;
            gray[j * width + i] = val;
        }
    }
}

 Python代码

    img = cv2.imread('1.jpg', 1)
    h, w, c = img.shape
    imgrgb = img[:, :, ::-1] # BGR2RGB
    imgrgb = np.transpos(img, (2, 0, 1)) # RGBRGB格式转成RRR  GGG  BBB
    imgrgb = imgrgb.reshape(1, -1)
    grayimg = np.zeros((h,w), np.uint8)
    grayimg = grayimg.reshape(1, -1)
    rgbdata = imgrgb.ctypes.data_as(ctypes.POINTER(ctypes.c_ubyte))
    graydata = grayimg.ctypes.data_as(ctypes.POINTER(ctypes.c_ubyte))
    width = ctypes.c_int(w)
    height = ctypes.c_int(h)
    mylib.GetGrayImage(rgbdata, graydata, width, height)
    grayimg = grayimg.reshape(h, w)
    cv2.imwrite('gray.jpg', grayimg)

         暂且列了这些应用,一些没涉及到的,可能根据这些用例可以类似处理,之后如果遇到新的使用案例再新增。 

参考资料:

python调用c++模块.so库, 互相回传数据(ctypes、pybind11) - 知乎 

python3调用c++动态库(linux) - 知乎

将Numpy数组通过ctypes模块传递到C++函数_大DDDDD的博客-CSDN博客_ctypes.data_as 

python调用C++,传递结构体与结构体指针,以及嵌套结构体_ABigCaiBird的博客-CSDN博客 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/134474.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《机器学习与应用》实验二:BP神经网络实验

文章目录 一、实验目的二、实验原理BP算法的数学描述三、程序四、实验结论一、实验目的 1、 熟悉MATLAB中神经网络工具箱的使用方法; 2、 通过在MATLAB下面编程实现BP网络逼近标准正弦函数,来加深对BP网络的了解和认识,理解信号的正向传播和误差的反向传递过程。 二、实验…

SAP MM物料与客户主数据的税分类

一&#xff0e;说明 在物料主数据、客户主数据中均有税分类的维护&#xff0c;税分类既不是税码也不代表税率&#xff0c;它们的作用是通过税务条件记录确定税码。所有的税分类在主数据中都是与国家相关的无组织机构数据&#xff0c;例如物料的销售组织有中国&#xff08;ZH&am…

智慧WMS立体仓库管理系统源码 基于springboot框架(已经测试完整带部署搭建教程)源码分享!

淘源码&#xff1a;国内知名的高品质源码免费下载平台 分享一套智慧WMS立体仓库管理系统源码&#xff0c;基于springboot框架 已经测试完整带部署搭建教程。&#xff08;MF00767&#xff09; 需要源码学习可私信我获取。 技术架构 技术框架&#xff1a;SpringBoot layui H…

ESLint插件的使用

官网地址 规范写代码的工具. 多人开发不同规则,提交代码一堆冲突 培养代码风格使用 vscode更改tab缩进空格数----设置—搜索tabsize—找到tab size—修改2(每次按下tab都缩进俩空格)—Vetur > Format Options: Tab Size这个也要修改为2 vscode搜索format----勾选Editor: Fo…

javaweb01--mysql的介绍和增删改查操作

文章目录Mysql的介绍和增删改查说明1. mysql的登陆和退出11 登陆1.2 退出2. SQL语法的简单介绍2.1 语法2.2 SQL分类3. SQL主要操作语句3.1 DDL:操作数据库3.1.1 查询3.1.2 创建数据库3.1.3 删除数据库3.1.4 使用数据库3.2 DDL:操作表3.2.1 查询表3.2.2 创建表3.2.3 数据类型3.2…

《梁启超家书》笔记二——一个人若是在舒服的环境中会消磨志气,那么在困苦懊丧的环境中也一定会消磨志气

目录 一、做事的态度 二、学习与未来 三、发挥其个性之特长&#xff0c;以靖献于社会 四、鼓励相信孩子 五、犯错 六、身体健康 七、做事 八、与费用相关 九、在困苦中求快活 十、让孩子自由决策与建议 十一、处事态度&#xff1a;不要悲观 十二、时事分析 一、做事…

在IDEA中获取文件绝对路径(通用方式)

package com.javase.reflect;/*** 关于文件路径问题* 以前我们都是在IDEA中&#xff0c;依据IDEA默认的当前路径&#xff1a;project的根来获取文件&#xff0c;但是这种方法有它的局限性&#xff0c;那就是当代码离开了IDEA* 换到了其他位置&#xff0c;我们就找不到文…

基于 Tensorflow 2.x 使用 MobileNetV2 微调模型优化训练花卉图像识别模型

一、模型微调 上篇文章我们通过搭建三层卷积模型&#xff0c;训练了花卉图像识别模型&#xff0c;最后经验证集验证后准确率大约为 75% &#xff0c;本篇文章对该数据集进行优化&#xff0c;提高识别的准确度。本篇文章中对于数据集的读取强化不做过多的介绍了&#xff0c;大家…

免拆机,Kindle固件版本5.10.3~5.13.3如何越狱?简单、易操作版

前言 之前有出过Kindle的越狱教程&#xff1a; 无需拆机&#xff0c;Kindle 全系列 5.12.2.2 ~ 5.14.2版本如何越狱&#xff1f;如何安装第三方插件 确实可以越狱&#xff0c;使用的漏洞也是&#xff1a; KindleDrip — From Your Kindle’s Email Address to Using Your C…

ubuntu18.04下mysql数据库C语言API封装

mysql C语言API操作数据库比较繁琐&#xff0c;可以将其封装起来&#xff0c;这样使用比较方便&#xff0c;下面是一种封装方式。 目录 1.连接封装 2.连接池封装 3.测试代码 1.连接封装 将数据库连接进行封装&#xff0c;主要提供如下接口&#xff1a; &#xff08;1&…

L2-030 冰岛人

2018年世界杯&#xff0c;冰岛队因1:1平了强大的阿根廷队而一战成名。好事者发现冰岛人的名字后面似乎都有个“松”&#xff08;son&#xff09;&#xff0c;于是有网友科普如下&#xff1a; 冰岛人沿用的是维京人古老的父系姓制&#xff0c;孩子的姓等于父亲的名加后缀&#x…

torchnet.meter使用教程

前言 最近项目开发过程中遇到了torchnet.metertorchnet.metertorchnet.meter来记录模型信息&#xff0c;搜了好多篇博客潦潦草草&#xff0c;没有一点干货&#xff0c;于是根据官方代码和官方文档&#xff0c;基于自己的理解&#xff0c;制定了使用教程: torchnet简介 torch…

一句话实现报表生成PDF同时通过outlook发送

元旦节快乐 哈喽&#xff0c;大家2023年好呀&#xff01; 今天&#xff0c;元旦最后一天&#xff0c;给大家分享什么好玩的示例呢&#xff1f; 让我来想想&#xff0c;嗯&#xff1f;这样可以吗&#xff1f;一句话就实现将报表生成PDF&#xff0c;同时可以编辑一些信息并通过…

【源码分享】java多用户B2B2C商城源码带WAP手机端源码

分享一款非常不错的java多用户B2B2C商城源码&#xff0c;带WAP手机端源码&#xff0c;源码地址在文末。 需要源码学习&#xff0c;可私信我获取。 一、技术构架&#xff1a; 开发语言&#xff1a; Java1.7 数 据 库 &#xff1a; MySQL5.5 数据库持久层&#xff1a;阿里巴巴…

车载诊断协议UDS——会话模式状态机Session

UDS之Session服务 会话模式管控是汽车电子诊断范畴很重要的两个状态机之一(另一个是安全访问),不同的会话模式是用来区分诊断服务执行权限。 一位非常尊敬的业内前辈曾举如下例子来形容这个状态机:不同的场景,喝对应的酒! 公司商务场合下,对应的酒是红酒;长辈酒桌上,对…

Redis 哨兵模式

哨兵是一个分布式系统&#xff0c;你可以在一个架构中运行多个哨兵进程&#xff0c;这些进程使用流言协议来接收关于Master主服务器是否下线的信息&#xff0c;并使用投票协议来决定是否执行自动故障迁移&#xff0c;以及选择哪个Slave作为新的Master。 一、哨兵模式概述 1.1…

ubuntu做系统常见出错处理方法1

1.不能分区解决办法&#xff08;安装ubuntu没有出现安装选项&#xff0c;也就是找不到硬盘分区怎么办?-爱码网&#xff09; 解决办法是进入bios模式(一般都是重启时反复按f12&#xff0c;不同电脑型号可自行查阅)把硬盘模式从raid调整为ahci(System configuration–&#xff…

方差和标准差的意义

文章目录案例&#xff1a;箭靶案例&#xff1a;身高案例&#xff1a;身高体重在此前一篇文章 《算法效果评估&#xff1a;均方根误差&#xff08;RMSE&#xff09;/ 标准误差》中&#xff0c;我们介绍了方差/标准差的计算方法&#xff0c;也点出了它们是用来“度量数据离散程度…

linux系统中wifi驱动的配置与编译实现方法

大家好&#xff0c;今天主要和大家聊一聊&#xff0c;如何使用linux系统中的WIFI驱动完成相应的实验。 目录 第一&#xff1a;WIFI驱动添加与编译方法 第二&#xff1a;将驱动代码添加到linux内核中 第三&#xff1a;配置Linux内核 第四&#xff1a;编译WIFI驱动 第一&…

YOLOv5更换骨干网络之 MobileNetV3

论文地址&#xff1a;https://arxiv.org/abs/1905.02244 代码地址&#xff1a;https://github.com/xiaolai-sqlai/mobilenetv3 我们展示了基于互补搜索技术和新颖架构设计相结合的下一代 MobileNets。MobileNetV3通过结合硬件感知网络架构搜索&#xff08;NAS&#xff09;和 N…