python调用C++动态库
- 生成C++动态库
- 1.编译成64位的dll库
- 2. C++ 模板
- 3. 示例
- Python调用带参数的动态库
- python与c数据类型
- int、long、float、double参数
- 参数声明
- 字符与字符串参数
- C分配内存与释放内存
- python与c的效率
生成C++动态库
生成C++ 动态库需要注意以下几点:
1.编译成64位的dll库
如果64位的python调用32位的dll会报下面的错误:
OSError: [WinError 193] %1 不是有效的 Win32 应用程序。
这种错误的原因基本上都是64为的python调用了32位的动态库。
解决的方法是生成64位的动态库。安装了visual studio可以使用:
x64的命令行窗口进行编译。编译命令是:
cl /LD hello.cpp
2. C++ 模板
头文件的模板如下:
//dll.h
#ifndef DLL_EXPORT
#define DECLDIR __declspec(dllimport)
#else
#define DECLDIR __declspec(dllexport)
#endif
c++源文件的模板如下:
//hello.cpp
#include "stdio.h"
#include <iostream>
#include <string.h>
#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
3. 示例
c++程序见第二节
python程序如下:
from ctypes import *
lib_path = './hello.dll'
dlllib = cdll.LoadLibrary(lib_path)
dlllib.hello()
Python调用带参数的动态库
第一节的示例是没有任何参数的,属于最简单的情况。如何传递以及返回参数,才是python调用动态库的要点。
python与c数据类型
在ctypes中定义python互相对应的参数类型。
类型 | python | c |
---|---|---|
布尔 | c_bool | bool |
整型 | c_int | int |
长整型 | c_long | long |
浮点 | c_float | float |
双精度 | c_double | double |
字符 | c_char | char |
宽字符 | c_wchar | wchar_t |
字符串 | c_char_p | char* |
宽字符串 | c_wchar_p | wchar_t * |
指针 | c_void_p | void * |
int、long、float、double参数
标准的dll调用需要明确传入参数和返回参数,否则系统会默认为所有参数为整型。比如:
//hello.cpp
#include "stdio.h"
#include <iostream>
#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
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)
此时程序返回的结果为:
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)
此时结果为:
201 3.1999998092651367
字符与字符串参数
传递单个字符,可以使用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)
需要注意的是,c++对宽字符的处理比较麻烦,如果返回的字符串被修改,需要特别注意,因为返回的可能是乱码。
------
pass_char A
pass_wchar 学
pass_str test string return
pass_wstr 真奇妙
A
学test string
世界真奇妙
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)
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]
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)
结果:
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
数组的大小是100001个,要选择的元素在最后一个。纯查找时间python为233653个时间单位,而c为1576。当然传递数组需要很长的时间。这是额外的花销。需要综合考虑是否采用动态库,因为参数准备与传递需要很大的花销。
更多推荐
python调用c++动态库
发布评论