ctypes cdll打开动态库,数据交换,调试

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ChangHongJian/article/details/77162668

最近在使用python调用c语言写的dll,遇到不少问题记录一下。

1 动态库有其他依赖的动态库

就比如说你生成的dll通过在linux ldd 看到有其他的依赖
这里写图片描述
但是如果直接调用你的dll文件

 cdll.LoadLibrary(param.fcdll)

可能会有如下的错误
OSError: /home/wpr/bin/anaconda4.3.0/lib/python3.6/lib-dynload/../../libgomp.so: versionGOMP_4.0’ not found (required by`
或者是类似
ctypes loading a c shared library that has dependencies 这里面的错误

这估计是因为使用python调用的时候会搜索python环境里面自以为有的库,总之应该要强制指定动态库的位置这样才可以
比如改成

CDLL("/usr/lib/x86_64-linux-gnu/libgomp.so.1", mode=RTLD_GLOBAL)
self.cdll = cdll.LoadLibrary(param.fcdll)

这样就没问题了。 另外上面的问题在windows下面是没有的

2 定义动态库

这个在linux下面好像很直接,在windows下面就需要个dllexport,具体看代码

#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#define  DLL_API _declspec(dllexport)
#else
#define  DLL_API
#endif // _WIN32

DLL_API void test(){
    ...
}

3 C语言中使用C++

首先在.h文件中写上必要的结构体,函数,设为A。A这个函数是在.cpp文件中一个函数中调用的,它实际上是在.c中。
.cpp 引用.h的时候要加上

extern "C" {
#include "xxx.h"
}

void test(){
    A();
}

这样才行。

另外上传我这次写程序需要的cmake文件,感觉还算是比较通用

cmake_minimum_required(VERSION 2.8)
project(python_use)

# target_compile_options(myLib PRIVATE -fPIC)
#
#add_compile_options(-fpic)
#
#set(CMAKE_C_FLAGS“$ {CMAKE_C_FLAGS} -fpic”)
#set(CMAKE_CXX_FLAGS“$ {CMAKE_CXX_FLAGS} -fpic”)

IF (WIN32)  
    MESSAGE(STATUS "Now is windows")  
ELSEIF (APPLE)  
    MESSAGE(STATUS "Now is Apple systens.")  
ELSEIF (UNIX)  
    add_compile_options(-fPIC)
ENDIF ()  

find_package(OpenMP REQUIRED)

if(OPENMP_FOUND)
    message("OPENMP FOUND")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_C_FLAGS}")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()

#set(EXCUTABLE_OUPT_PATH ${PROJECT_SOURCE_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

set(SRC_DIR ${PROJECT_SOURCE_DIR}/)

include_directories(${SRC_DIR}/src)
include_directories(${CMAKE_CURRENT_LIST_DIR}/src/liblinear/)

include(${CMAKE_CURRENT_LIST_DIR}/src/liblinear/liblinear.cmake)
#include_directories()
#LINK_DIRECTORIES()

file(GLOB_RECURSE CURRENT_HEADERS  ${SRC_DIR}/src/*.h)
file(GLOB CURRENT_SOURCES  ${SRC_DIR}/src/*.cpp ${SRC_DIR}/src/*.c)

add_library(python_use SHARED ${CURRENT_SOURCES} )
target_link_libraries(python_use liblinear)

第二次在后面改了些

file(GLOB_RECURSE CURRENT_HEADERS ${SRC_DIR}/src/*.h ${SRC_DIR}/lbf/*.h)
file(GLOB CURRENT_SOURCES ${SRC_DIR}/src/*.cpp ${SRC_DIR}/src/*.c )

file(GLOB LBF_SRCS ${SRC_DIR}/src/lbf_src/*.cpp )
source_group(lbf FILES ${LBF_SRCS})
# 这个 lbf srcs 也需要加入add library中

add_library(3000fps_python_use SHARED ${CURRENT_SOURCES} ${LBF_SRCS})
target_link_libraries(3000fps_python_use liblinear ${OpenCV_LIBS} )

4 借助ctypes的数据交换(一个传送图片的例子)

其实基本框架配好之后数据交换就是中投戏了。不过关于ctypes的介绍比较多,这里就介绍一些难搜到的东西。

传结构体参数

比如你的C中是这样的结构体(其实我这个是传图片用的)

struct global_param {
    int img_n;
    int * img_wh;  // 如果地址是连续的即使python中是数组最好也用一个指针
    unsigned char ** imgs; // 接受list,地址不连续
    char * str;
};  
# 首先定义一个一样的结构体
class set_global_param_param(Structure):
    _fields_ = [
        ("img_n", c_int),
        ("img_wh", POINTER(c_int)),
        ("imgs", POINTER(POINTER(c_uint8))),
        ("str", c_char_p),

# 下面这个粗略写一下,大家应该能明白
imgs_wh=np.zeros( (self.n_imgs,2),np.int32 )
imgs_p= ( POINTER(c_uint8)*self.n_imgs )() # 这个用法找了很久

...
imgs_p[i]=img.ctypes.data_as(POINTER(c_uint8))
...

# 然后如下使用
pm=set_global_param_param()
pm.img_n=self.n_imgs
pm.img_wh=imgs_wh.ctypes.data_as(POINTER(c_int))
pm.imgs= imgs_p     
pm.str=(c_char * 100)().value = bytes("xxxxx",encoding="ascii") 
# 其实就是用b"xxxxxx"

这样就可以实现把python中加载的图片地址传给C语言了,然后用opencv操作,就很方便了。
给个处理的示例

void handle_img_mat(uint8_t ** pimgs,int img_n, int * img_wh, vector<Mat>& imgs) {
    imgs.resize(img_n);
    for (int i = 0; i < img_n; i++) {
        Mat img = cv::Mat(img_wh[i * 2], img_wh[i * 2 + 1], CV_8UC1, pimgs[i]);
        imgs[i] = img;
        //cv::imshow("pic", img);
        //cv::waitKey(3000);
        //break;
    }

}

其中字符串那个弄了不少时间,参考的博客有
https://stackoverflow.com/questions/2365411/python-convert-unicode-to-ascii-without-errors
https://stackoverflow.com/questions/7237133/create-string-buffer-throwing-error-typeerror-str-bytes-expected-instead-of-str

5 使用VS调试

目前还不会二者同时调试,只能通过vs运行 python程序,然后到了加载dll那个地方就会进入要调试的c++程序。
过程是:
调试->xxxx 属性 ->
这里写图片描述

这样直接运行就可以了

弄了很久,不断搜资料并尝试研究才成功,希望对大家有帮助

展开阅读全文

没有更多推荐了,返回首页