实现python调用C++的动态链接库 案例分享和解析

实现python调用C++的动态链接库

本文把在用python调用C++的动态链接库 中,搜集了不少资料,有些博客讲的非常好,所以就把这些内容进行了一定的汇总和总结。

1.本文不讲如何编译动态链接库, 这个问题要自己学习一下如何编译,如 cmake

2.本文只讲案例,这些都是本人自己借鉴过的。

1.如题,首先为什么不调用静态链接库?

因为不支持。

2.其次,总的流程是什么?

很简单,
1.将想要暴露的接口编译成动态链接库,然后将包含静态链接库(.so)以及对应的接口函数的头文件(.h)放到python调用的文件夹里即可。
2.python调用的时候,本文使用的是ctypes 包,能够很容易的调用 so文件,同时要注意文件解析。

3.先上几个参考的例子:

(1)我这里参考了这个例子的  python 将数组传递给C++

INPUT = c_int * 4
# 实例化一个长度为2的整型数组
input = INPUT()
# 为数组赋值(input这个数组是不支持迭代的)
input[0] = 11
input[1] = 2
input[2] = 3
input[3] = 4
dll.teststring.restype = c_char_p
# bytes(aaaa, encoding="utf-8")
a = dll.teststring(input,4)
 
 
MYLIBDLL char* teststring(int* plus1, int len);
 
char* teststring(int* plus1,int len) {
 
 
    for (int i = 0; i < len; i++) {
        printf("%d \n", plus1[i]);
    } 
 
 
 
 
    Mat mat;
    //加载图片  
    mat = imread("bgs.jpg", CV_LOAD_IMAGE_COLOR);
    printf("a %d %d", mat.rows, mat.cols);
    //if (!mat.empty()) {
 
 
    int m, n;
    n = mat.cols * 3;
    m = mat.rows;
    unsigned char *data = (unsigned char*)malloc(sizeof(unsigned char) * m * n);
    int p = 0;
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            data[p] = mat.at<unsigned char>(i, j);
            p++;
        }
    }
    *plus1 = p;
    return (char*)data;
}

其次,参考了怎么让数组传回,我目前没有直接用数组返回成功过。

(2)看了很多案例,都是用结构体struct 进行封装然后转换 

c++ 部分:

#include <iostream>
using namespace std;
typedef unsigned char BYTE;
#define MAX_COUNT 20
  
struct tagOutCardResult_py
{
    BYTE                            cbCardCount;                    
    BYTE                            cbResultCard1;
    BYTE                            cbResultCard2;
    BYTE                            cbResultCard3;
    BYTE                            cbResultCard4;
    BYTE                            cbResultCard5;
    BYTE                            cbResultCard6;
    BYTE                            cbResultCard7;
    BYTE                            cbResultCard8;
    BYTE                            cbResultCard9;
    BYTE                            cbResultCard10;
    BYTE                            cbResultCard11;
    BYTE                            cbResultCard12;
    BYTE                            cbResultCard13;
    BYTE                            cbResultCard14;
    BYTE                            cbResultCard15;
    BYTE                            cbResultCard16;
    BYTE                            cbResultCard17;
    BYTE                            cbResultCard18;
    BYTE                            cbResultCard19;
    BYTE                            cbResultCard20;
};
  
struct tagOutCardResult
{
    BYTE                            cbCardCount;                    
    BYTE                            cbResultCard[MAX_COUNT];        
    void clear()
    {
        cbCardCount = 0;
        for (int nIdx = 0;nIdx < MAX_COUNT;++nIdx)
        {
            cbResultCard[nIdx] = 0;
        }
    }   
    void topy(tagOutCardResult_py* ppy)
    {
        cout<<"topy function begin"<<endl;
        ppy->cbCardCount = cbCardCount;
        cout<<"topy function 1"<<endl;
        ppy->cbResultCard1 = cbResultCard[1 - 1];
        cout<<"topy function 2"<<endl;
        ppy->cbResultCard2 = cbResultCard[2 - 1];
        ppy->cbResultCard3 = cbResultCard[3 - 1];
        ppy->cbResultCard4 = cbResultCard[4 - 1];
        ppy->cbResultCard5 = cbResultCard[5 - 1];
        ppy->cbResultCard6 = cbResultCard[6 - 1];
        ppy->cbResultCard7 = cbResultCard[7 - 1];
        ppy->cbResultCard8 = cbResultCard[8 - 1];
        ppy->cbResultCard9 = cbResultCard[9 - 1];
        ppy->cbResultCard10 = cbResultCard[10 - 1];
        ppy->cbResultCard11 = cbResultCard[11 - 1];
        ppy->cbResultCard12 = cbResultCard[12 - 1];
        ppy->cbResultCard13 = cbResultCard[13 - 1];
        ppy->cbResultCard14 = cbResultCard[14 - 1];
        ppy->cbResultCard15 = cbResultCard[15 - 1];
        ppy->cbResultCard16 = cbResultCard[16 - 1];
        ppy->cbResultCard17 = cbResultCard[17 - 1];
        ppy->cbResultCard18 = cbResultCard[18 - 1];
        ppy->cbResultCard19 = cbResultCard[19 - 1];
        ppy->cbResultCard20 = cbResultCard[20 - 1];
        cout<<"topy function end"<<endl;
    }
};
  
class TestLib
{
    public:
        void display(tagOutCardResult& ret);
};
void TestLib::display(tagOutCardResult& ret) {
    ret.cbCardCount = 3;
    ret.cbResultCard[0] = 1;
    ret.cbResultCard[1] = 50;
    ret.cbResultCard[2] = 100;
  
    cout<<"First display aaa ";
    cout<<"hello ";
    cout<<"world ";
}
  
extern "C" {
    TestLib oGameLogic;
    void display(tagOutCardResult_py* ret_py) {
        tagOutCardResult oRet;
        oGameLogic.display(oRet);
        cout<<"before topy"<<endl;
        oRet.topy(ret_py);
        cout<<"after topy"<<endl;
        cout<<"in cpp:ret_py->cbCardCount:"<<ret_py->cbCardCount<<endl;
        cout<<"in cpp:ret_py->cbResultCard1:"<<ret_py->cbResultCard1<<endl;
        cout<<" this:" << ret_py << endl;
    }
}

python 部分:

import ctypes
  
class tagOutCardResult_py(ctypes.Structure):
 _fields_ = [("cbCardCount", ctypes.c_ubyte), \
("cbResultCard1", ctypes.c_ubyte), \
("cbResultCard2", ctypes.c_ubyte), \
("cbResultCard3", ctypes.c_ubyte), \
("cbResultCard4", ctypes.c_ubyte), \
("cbResultCard5", ctypes.c_ubyte), \
("cbResultCard6", ctypes.c_ubyte), \
("cbResultCard7", ctypes.c_ubyte), \
("cbResultCard8", ctypes.c_ubyte), \
("cbResultCard9", ctypes.c_ubyte), \
("cbResultCard10", ctypes.c_ubyte), \
("cbResultCard11", ctypes.c_ubyte), \
("cbResultCard12", ctypes.c_ubyte), \
("cbResultCard13", ctypes.c_ubyte), \
("cbResultCard14", ctypes.c_ubyte), \
("cbResultCard15", ctypes.c_ubyte), \
("cbResultCard16", ctypes.c_ubyte), \
("cbResultCard17", ctypes.c_ubyte), \
("cbResultCard18", ctypes.c_ubyte), \
("cbResultCard19", ctypes.c_ubyte), \
("cbResultCard20", ctypes.c_ubyte)]




import ctypes
so = ctypes.cdll.LoadLibrary
lib = so("./libpycallclass.so")
ERROR_MSG('display(\)')
ret = tagOutCardResult_py(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
ERROR_MSG("before lib.display(ctypes.byref(ret))")
lib.display(ctypes.byref(ret))
ERROR_MSG("after lib.display(ctypes.byref(ret))")
ERROR_MSG('#######################################################################################')
ERROR_MSG(ret)
ERROR_MSG(ret.cbCardCount)
ERROR_MSG(ret.cbResultCard1)
ERROR_MSG(ret.cbResultCard2)
ERROR_MSG(ret.cbResultCard3)
ERROR_MSG(type(ret))

实际上,有上面可以看到,可以利用结构体的引用,实现数据的传递,这样的做法好处在于可以避免内存泄漏。

(3)当然也有用指针进行传递的,但是也是基于结构体包装:

python 部分:

# 返回结构体
import ctypes
path = r'E:\01_Lab\VisualStudioLab\cpp_dll\cpp_dll\Debug\cpp_dll.dll'
dll = ctypes.WinDLL(path)
 
class StructPointer(ctypes.Structure):  
    _fields_ = [("name", ctypes.c_char * 20), 
                ("age", ctypes.c_int),
                ("arr", ctypes.c_int * 3)]  
    
dll.test.restype = ctypes.POINTER(StructPointer)  
p = dll.test()
 
print(p.contents.name)
print(p.contents.age)
print(p.contents.arr[0])
print(p.contents.arr[1])
print(p.contents.arr[2])

c++部分代码: 可以看到这里是返回结构体的指针的,我看到有同学表示这里有内存泄漏,自己没试过,感兴趣的同学可以试试

typedef struct StructPointerTest  
{  
    char name[20];  
    int age;
	int arr[3];
}StructPointerTest, *StructPointer; 



DLLEXPORT StructPointer __stdcall test()    // 返回结构体指针  
{   
    StructPointer p = (StructPointer)malloc(sizeof(StructPointerTest));   
    strcpy(p->name, "Joe");  
    p->age = 20;  
	p->arr[0] = 3;
	p->arr[1] = 5;  
	p->arr[2] = 10;
		
    return p;   
} 

(4)另外一个问题在于写C++代码的时候,由于C++有重载功能,所以编译器在编译的时候是会把函数改名的,这个时候就需要强调需要被python调用的函数是以c的方式编译。需要用如下方式:

extern "C"
{
    float test()
    {
        printf("hello cpp");
        return 1;
    }
}

也就是说extern "C" 这句非常重要 因为在编译的时候被改名了,导致我们最终会找不到这个函数。

这边的例子是如何传opencv 图片 的例子:

c++ 代码, 就是把图片数组传入,然后重新赋值

float test(int height, int width, uchar* frame_data)
{
  cv::Mat image(height, width, CV_8UC3);
  uchar* pxvec =image.ptr<uchar>(0);
  int count = 0;
  for (int row = 0; row < height; row++)
  {
    pxvec = image.ptr<uchar>(row);
    for(int col = 0; col < width; col++)
    {
      for(int c = 0; c < 3; c++)
      {
        pxvec[col*3+c] = frame_data[count];
        count++;
      }
    }
  }
  float value = 0.2;
  return value;
}

python 代码,对应的 你需要了解  ctypes 的c和python的转换格式,网上很多 

import ctypes
import cv2
#load
ll = ctypes.cdll.LoadLibrary
# 动态链接库
lib = ll("./lib/libtest.so")
#设置这个函数的返回类型
lib.test.restype = ctypes.c_float
#读图片
frame = cv2.imread('test.jpg')
#将图片转化成array 并让c++ 接受的格式
frame_data = np.asarray(frame, dtype=np.uint8)
frame_data = frame_data.ctypes.data_as(ctypes.c_char_p)
#返回结果
value = lib.test(frame.shape[0], frame.shape[1], frame_data)
print value

当然注意,以上这段可以借鉴的是传图片数组。

欢迎批评指正,谢谢!

感谢一下博文,他们都写的非常好。

参考:https://blog.csdn.net/u011021773/article/details/83188012

https://www.jb51.net/article/156159.htm

https://www.jb51.net/article/156164.htm

https://blog.csdn.net/inch2006/article/details/79908802

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值