• 1、前言
  • 2、介绍
  • 3、环境安装
  • 3.1 gcc安装
  • 3.2 cmake安装
  • 4、编译
  • 4.1 CMakeLists.txt
  • 4.2 编译
  • 4.3 检查
  • 5、python中调用
  • 6、补充:一个更简单的方法编译共享链接库



该系列文章:


(一)python调用c++代码《从C++共享链接库编译到python调用指南》


(二)ndarray(Python)与Mat(C++)数据的传输


(三)C++结构体与python的传输

1、前言

对于某些时候,我们希望能在python中调用c++代码,或许是为了追求速度,或许是为了调用现成的c++代码。

网上也有很多相关方面的教程,但他们的c++代码仅仅为一个函数或者一个类,情况比较简单。

我找到了一个不错的c++项目,但是我没有能力用python重写,所以我将c++中的main函数写成一个类,并希望导出为共享链接库(.so文件),在python中调用。

我的难点在于我希望导出的这个类,使用了第三方库OpenCV,以及这个类还使用了其他的类,情况一下就复杂了。

实际上这也符合真实情况,如果我只想调用c++实现的一个函数或者单纯的类,为什么不直接用python写呢?实际情况才是如同上面我讲的那样,情况复杂。

2、介绍

python 与 C 同时操作共享内存 c++和python共享内存_c++

注意:opencv.cpp是作者自己写的一个类(类似于RPPG.cpp),而OpenCV是第三方库,不要混淆了

三个类都使用了OpenCV第三方库,同时HB使用了RPPG类和opencv类,RPPG使用了opencv类,而我要导出HB类,使其可以在python中调用,依赖关系复杂了。

所以,我们使用cmake来帮助编译so文件

3、环境安装

首先我们在Ubuntu20.04中编译( (一)Ubuntu安装详细教程(从镜像制作到NVIDIA驱动安装全流程)——超详细的图文教程)

3.1 gcc安装

先查看是否安装gcc:

python 与 C 同时操作共享内存 c++和python共享内存_c++_02

如果没有安装:

# 在终端中,依次执行
sudo apt-get update
sudo apt-get install build-essential gdb

3.2 cmake安装

先查看是否安装cmake:

python 与 C 同时操作共享内存 c++和python共享内存_c++_03


如果没有安装,请参阅 Kitware APT存储库中适用于您的平台的说明

4、编译

# .hpp 头文件,用于申明
# .cpp 实现头文件中申明的函数或类
-project
--HB.cpp
--HB.hpp
--RPPG.cpp
--RPPG.hpp
--opencv.cpp
--opencv.hpp
--......

首先我的结构目录如上,在节2中给出了之间的关系,我们的目的是导出HB.cpp为so文件

4.1 CMakeLists.txt

我们知道编译时会指定许多参数,CMakeLists.txt就是告诉cmake我们编译时的参数设定。

我们在project文件夹下新建CMakeLists.txt文件,内容如下:

cmake_minimum_required(VERSION 3.0.0)	# 最小版本
project(hbp VERSION 0.1.0)  # 项目名称

set(CMAKE_CXX_FLAGS "-std=c++11")   # 添加c++11标准

find_package(OpenCV REQUIRED) # 添加OpenCV库
include_directories(${OpenCV_INCLUDE_DIRS})

add_library(opencv SHARED opencv.cpp)	# 把opencv.cpp导出为链接库,SHARED指定为共享链接库
target_link_libraries(opencv ${OpenCV_LIBS})	#因为opencv.cpp使用了OpenCV,所以将OpenCV链接到opencv中,相当于告诉opencv去哪儿找OpenCV

add_library(RPPG SHARED RPPG.cpp)
target_link_libraries(RPPG ${OpenCV_LIBS})	# RPPG也使用了OpenCV库,也要链接

add_library(HB SHARED HB.cpp)
target_link_libraries(HB ${OpenCV_LIBS})	# HB也使用了OpenCV库

target_link_libraries(RPPG opencv)	# RPPG还使用了opencv类
target_link_libraries(HB RPPG) # HB使用了RPPG(同时RPPG链接了opencv,相当于HB间接链接了opencv)

可以看出:

  1. 第三方库是通过target_link_libraries直接链接
  2. 自定义类要先通过add_library定义为共享链接库,后面再通过target_link_libraries链接

4.2 编译

在project中新建build文件夹:

-project
--build/
--CMakeLists.txt
--HB.cpp
--HB.hpp
--RPPG.cpp
--RPPG.hpp
--opencv.cpp
--opencv.hpp
--......

再终端中进入build/,执行命令:$ cmake ..

python 与 C 同时操作共享内存 c++和python共享内存_opencv_04


再执行:$ make

python 与 C 同时操作共享内存 c++和python共享内存_OpenCV_05


然后就得到了想要的HB.so文件:

python 与 C 同时操作共享内存 c++和python共享内存_python 与 C 同时操作共享内存_06


(会自动加lib-前缀,所以libHB.so就是编译好的文件)

4.3 检查

编译时成功不代表真的成功,我们需要检查一下。

执行命令$ ldd -r libHB.so

python 与 C 同时操作共享内存 c++和python共享内存_OpenCV_07


python 与 C 同时操作共享内存 c++和python共享内存_python_08


这代表成功了。

失败了是什么样的?

如果我直接按照python调用C++中的函数【最简明教程】编译so文件:$ g++ -o HB.so -shared -fPIC HB.cpp得到HB.so文件,现在检查一下这个有没有问题$ ldd -r HB.so

python 与 C 同时操作共享内存 c++和python共享内存_c++_09


可以看到出现大量的"undefined symbol:“,从后面的_ZN2cv8fastFreeEPv可以看出是缺少OpenCV的链接,导致使用的OpenCV函数为"undefined symbol:”,同理还可以看到“RPPG”等。如果你想查看是具体什么函数,你可以执行命令:

c++filt _ZN2cv8fastFreeEPv就可以查看到后面的一串到底代表哪个函数

python 与 C 同时操作共享内存 c++和python共享内存_python_10

5、python中调用

直接给代码:

import ctypes
dll=ctypes.cdll.LoadLibrary

# 加载so链接库
lib=dll("./libHB.so")

# 这里是调用HB类中的load函数
lib.load()

可以看到C++中HB.load()函数执行成功会打印字符串:

python 与 C 同时操作共享内存 c++和python共享内存_python_11


验证一下,运行python代码,ok!

python 与 C 同时操作共享内存 c++和python共享内存_python_12

6、补充:一个更简单的方法编译共享链接库

实际上我们可以使用一下命令将HB.cpp编译为共享链接库:

g++ -std=c++11 \
    -o HB.so \
    -shared \
    -fPIC HB.cpp opencv.cpp RPPG.cpp \
    `pkg-config --cflags --libs opencv`

如此便成功将HB.cpp编译并保存在HB.so文件

下面简单解释一下

g++ -std=c++11 \  # 指定了c++11标准
    -o HB.so \	# output
    -shared \	# 创建共享链接库
    #链接使用到的cpp,整个项目使用到了这三个cpp,所以全写上
    -fPIC HB.cpp opencv.cpp RPPG.cpp \ #
    # 链接使用的第三方库opencv,你可以通过pkg-config opencv --modversion看看是否正确安装了opencv,如果是则会显示版本号比如3.4.11
    `pkg-config --cflags --libs opencv`

注意:pkg-config --cflags --libs opencv 前后的“撇”不是单引号,是左上角1旁边的按键符号