• python调用c++动态库


    生成C++动态库

    生成C++ 动态库需要注意以下几点:

    1.编译成64位的dll库

    如果64位的python调用32位的dll会报下面的错误:

    OSError: [WinError 193] %1 不是有效的 Win32 应用程序。
    
    • 1

    这种错误的原因基本上都是64为的python调用了32位的动态库。
    解决的方法是生成64位的动态库。安装了visual studio可以使用:
    在这里插入图片描述
    x64的命令行窗口进行编译。编译命令是:

    cl /LD hello.cpp
    
    • 1

    2. C++ 模板

    头文件的模板如下:

    //dll.h
    #ifndef DLL_EXPORT
    #define DECLDIR __declspec(dllimport)
    #else
    #define DECLDIR __declspec(dllexport)
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    c++源文件的模板如下:

    //hello.cpp
    #include "stdio.h"
    #include 
    #include 
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    #define DLL_EXPORT 
    #include "dll.h"
    
    using namespace std;
    
    #pragma execution_character_set("utf-8")
    
    DECLDIR void hello(void){
    	printf("hello world");
    	}
    DECLDIR int add_int(int a,int b){
    	return a+b;
    }
    DECLDIR float add_float(float a,float b){
    	return a+b;
    }
    DECLDIR double add_double(double a,double b){
    	return a+b;
    }
    DECLDIR char pass_char(char c){
    	printf("%c\n",char(c));
    	return c;
    }
    DECLDIR wchar_t pass_wchar(wchar_t  wc){
    	wcout.imbue(locale("chs"));
    	wcout<< wc;
    	return wc;
    }
    DECLDIR char* pass_str(char *s){
    	printf("%s\n",s);
    	return strcat(s,"return");
    }
    DECLDIR wchar_t* pass_wstr(wchar_t*s,wchar_t wc){
    	wcout<< s;
    	wchar_t *p = wcschr(s,wc);
    	return p;
    	
    }
    
    DECLDIR int* get_memory(int n)
    {
        int *p = new int[n];
    	for(int i = 0; i < n; i++)
    	{
    			p[i] = i;
    	}
    	return p;
    }
     
    DECLDIR void free_memory(int *p)
    {
    	if(p)
    		delete [] p;
    }
    DECLDIR int  find_num(int target,int *p,int n){
    	int i;
    	for (i=0;i<n;i++){
    		if (p[i]== target)
    			return i;
    	}
    	return -1;
    	}
    
    #ifdef __cplusplus
    }
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    3. 示例

    c++程序见第二节
    python程序如下:

    from ctypes import *
    lib_path = './hello.dll'
    dlllib = cdll.LoadLibrary(lib_path)
    dlllib.hello()
    
    • 1
    • 2
    • 3
    • 4

    Python调用带参数的动态库

    第一节的示例是没有任何参数的,属于最简单的情况。如何传递以及返回参数,才是python调用动态库的要点。

    python与c数据类型

    在ctypes中定义python互相对应的参数类型。

    类型pythonc
    布尔c_boolbool
    整型c_intint
    长整型c_longlong
    浮点c_floatfloat
    双精度c_doubledouble
    字符c_charchar
    宽字符c_wcharwchar_t
    字符串c_char_pchar*
    宽字符串c_wchar_pwchar_t *
    指针c_void_pvoid *

    int、long、float、double参数

    标准的dll调用需要明确传入参数和返回参数,否则系统会默认为所有参数为整型。比如:

    //hello.cpp
    #include "stdio.h"
    #include 
     
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    #define DLL_EXPORT 
    #include "dll.h"
    
    using namespace std;
     
    DECLDIR void hello(void){
    	printf("hello world");
    	}
    DECLDIR int add_int(int a,int b){
    	return a+b;
    }
    DECLDIR float add_float(float a,float b){
    	return a+b;
    }
    DECLDIR double add_double(double a,double b){
    	return a+b;
    }
    
    #ifdef __cplusplus
    }
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    from ctypes import *
    lib_path = './hello.dll'
    dlllib = cdll.LoadLibrary(lib_path)
    ret_i =dlllib.add_int(1,200)
    ret_f = dlllib.add_float(c_float(1.1),c_float(2.1))
    print(ret_i,ret_f)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    此时程序返回的结果为:
    201 1094533517

    参数声明

    解决办法是显式声明传入和返回参数。

    from ctypes import *
    lib_path = './hello.dll'
    dlllib = cdll.LoadLibrary(lib_path)
    ret_i =dlllib.add_int(1,200)
    dlllib.add_float.argtypes=[c_float,c_float]
    dlllib.add_float.restype = c_float
    ret_f = dlllib.add_float(c_float(1.1),c_float(2.1))
    print(ret_i,ret_f)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    此时结果为:

    201 3.1999998092651367
    
    • 1

    字符与字符串参数

    传递单个字符,可以使用c_char或者c_wchar,如果要传递字符串则需要c_char_p和c_wchar_p:

    from ctypes import *
    lib_path = './hello.dll'
    dlllib = cdll.LoadLibrary(lib_path)
    dlllib.pass_char.argtypes=[c_char]
    dlllib.pass_char.restype = c_char
    ret_c = dlllib.pass_char(b"A")
    dlllib.pass_wchar.argtypes=[c_wchar]
    dlllib.pass_wchar.restype = c_wchar
    ret_w = dlllib.pass_wchar("学")
    
    dlllib.pass_str.argtypes=[c_char_p]
    dlllib.pass_str.restype = c_char_p
    
    ret_s = dlllib.pass_str(b"test string ")
    dlllib.pass_wstr.argtypes=[c_wchar_p,c_wchar]
    dlllib.pass_wstr.restype = c_wchar_p
    ret_sw = dlllib.pass_wstr("世界真奇妙","好")
    
    print("------")
    print("pass_char",str(ret_c.decode("utf8")))
    print("pass_wchar",ret_w)
    print("pass_str",str(c_char_p(ret_s).value.decode("utf8")))
    print("pass_wstr",ret_sw)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    需要注意的是,c++对宽字符的处理比较麻烦,如果返回的字符串被修改,需要特别注意,因为返回的可能是乱码。

    ------
    pass_char A
    pass_wchar 学
    pass_str test string return
    pass_wstr 真奇妙
    A
    学test string 
    世界真奇妙
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    C分配内存与释放内存

    分配内存后,返回的是一个指针。

    from ctypes import *
    lib_path = './hello.dll'
    dlllib = cdll.LoadLibrary(lib_path)
    dlllib.get_memory.argtypes=[c_int]
    dlllib.get_memory.restype = POINTER(c_int)
    ret_int = dlllib.get_memory(20)
    print("get:",ret_int)
    int_array = [ret_int[i] for i in range(20)]
    print(int_array)
    
    dlllib.free_memory.restype = c_void_p
    dlllib.free_memory(ret_int)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    get: <__main__.LP_c_long object at 0x00000272E0718040>
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
    
    • 1
    • 2

    python与c的效率

    使用python与C混合编程的意义在于C的处理速度快,毕竟python是解释型语音。比如通过循环查找数组里面的数值:

    from ctypes import *
    import line_profiler
    import sys
    
    def c_find(a):
        lib_path = './hello.dll'
        dlllib = cdll.LoadLibrary(lib_path)
        dlllib.find_num.argtypes=[c_int,POINTER(c_int),c_int]
        dlllib.find_num.restype = c_int
    
        ret_f = dlllib.find_num(5,a,len(a))
        return ret_f
    
    def p_find(a):
        for i in range(len(a)):
            if a[i]==5:
                return i
        return -1
    
    def main():
        a = [20]*100000
        a.append(5)
        arr_d = (c_int * len(a))(*(i for i in a))
        lib_path = './hello.dll'
        dlllib = cdll.LoadLibrary(lib_path)
        dlllib.find_num.argtypes=[c_int,POINTER(c_int),c_int]
        dlllib.find_num.restype = c_int
        dlllib.find_num(5,arr_d,len(arr_d))
        p_find(a)
    
    profile = line_profiler.LineProfiler(main)  
    profile.enable()  
    main()
    profile.disable() 
    profile.print_stats(sys.stdout)  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    结果:

    Timer unit: 1e-07 s
    
    Total time: 0.0549094 s
    File: p1.py
    Function: main at line 21
    
    Line #      Hits         Time  Per Hit   % Time  Line Contents
    ==============================================================
        21                                           def main():
        22         1       3110.0   3110.0      0.6      a = [20]*100000
        23         1       2200.0   2200.0      0.4      a.append(5)
        24         1     299069.0 299069.0     54.5      arr_d = (c_int * len(a))(*(i for i in a))
        25         1         11.0     11.0      0.0      lib_path = './hello.dll'
        26         1       9016.0   9016.0      1.6      dlllib = cdll.LoadLibrary(lib_path)
        27         1        428.0    428.0      0.1      dlllib.find_num.argtypes=[c_int,POINTER(c_int),c_int]
        28         1         31.0     31.0      0.0      dlllib.find_num.restype = c_int
        29         1       1576.0   1576.0      0.3      dlllib.find_num(5,arr_d,len(arr_d))
        30         1     233653.0 233653.0     42.6      p_find(a)
    
    
    Process finished with exit code 0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    数组的大小是100001个,要选择的元素在最后一个。纯查找时间python为233653个时间单位,而c为1576。当然传递数组需要很长的时间。这是额外的花销。需要综合考虑是否采用动态库,因为参数准备与传递需要很大的花销。

  • 相关阅读:
    如何搭建一个自己的音乐服务器
    七夕节赚取徽章啦
    如何设计一个优惠券系统
    Task06|连接|joyfulpandas|组队学习 2022.8月组队学习
    在项目中,为什么有 全英文大写的 变量?
    记一次中兴新支点NewStartHA虚拟机安装
    SpringMVC概述与简单使用
    制造业MES系统如何管理生产车间
    抖音全接口API
    《异常检测——从经典算法到深度学习》27 可执行且可解释的在线服务系统中重复故障定位方法
  • 原文地址:https://blog.csdn.net/weixin_42272768/article/details/126468285