python 利用ctypes调用c/c++库一点例子
偶然花了几天时间第一次用ctypes编写相关接口,这个虽然不能直接生成python库,但直接调用c/c++库的(个人不太想区分c/c++,一般情况混合用),感觉ctypes是个好东西,而且使用简单,用过了就会发现是真的喜欢,大致记录下,以免忘记(当然好工具也挺多,比如docker,数据库mysql和搭建服务器的 Nginx +gunicorn + flask等工具,个人有点懒,不太想记录,随缘记录了),本例子只是测试python和c/c++数组等数据交互和调用可行,具体需要求根据各自修改:
1. c/c++代码
简单例子,就两个文件test.cpp,test.h,直接贴上来,不太想改动什么,名字随意;
test.h:
头文件:test.h,通过结构体和python端传递数据,当然python端要对应定义类似的结构(听说名字可以不同),主要用指针操作(个人挺喜欢指针的),共享内存,免去拷贝(除非不得已),这里python直接调用vs编译后的dll库,注意python中调用库的路径要匹配上。
#pragma once
//#define ENCODE_API __attribute__((visibility ("default")))//linux 则声明这个,类似MYDLL_API
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
#include <iostream>
#include <vector>
typedef struct asrInOutput_ {
const char* modelName = "";//model file
float* inputF[3] = { NULL ,NULL ,NULL };//float:intput1,intput3,intput4
int* inputI = NULL;//int:input2
int inputSize[4] = { 0,0,0,0 };
float* outputF[3] = { NULL,NULL,NULL };
int outputFSize[3] = { 0,0,0 };
int outBufNumberMax[3] = { 0,0,0 };//new obj number
} asrInOutput;
#ifdef __cplusplus
extern "C" {
#endif
MYDLL_API int asrProcess(asrInOutput* asr);
MYDLL_API void asrRealeas(asrInOutput* asr);
int newOutputBuf(asrInOutput* asr);
MYDLL_API int testInputOutput(asrInOutput* asr);
MYDLL_API int testPrint();
#ifdef __cplusplus
}
#endif
test.cpp:
要注意的是,因为输出的多个数组大小可能不确定,所以例子以这个为前提条件来设计的,具体大小通过另外一个数字来标记,联合使用,这也是使用指针需要注意的地方,要清楚指针指向空间多大、类型什么。
#include "test.h"
using namespace std;
#define toStr(name) (#name)//linux
#define F_OR_INT_PRINTT
#ifdef F_OR_INT_PRINTT
typedef float T_NAME;
#else
typedef int T_NAME;
#endif // F_OR_INT_PRINTT
//template <class T_NAME>
void printArrayData1(std::vector<T_NAME>& data, char* name)
{
std::cout << "\n***********" << name << ".size()==" << data.size() << "**********" << std::endl;
char* spData = (char*)"";
if (std::is_same<T_NAME, int>::value)
{
std::cout << "here int type emerges" << std::endl;
spData = (char*)"%d ";
}
else if (std::is_same<T_NAME, float>::value)
{
std::cout << "here float type emerges" << std::endl;
spData = (char*)"%.2f ";
}
for (size_t i = 0; i < data.size(); i++)
{
if (20 == i % 21) printf("\n");
printf(spData, data[i]);
}
std::cout << "" << std::endl;
}
void printFloat(float* data, int len)
{
for (size_t i = 0; i < len; i++)
{
if (20 == i % 21) printf("\n");
printf("%.2f ", data[i]);
}
std::cout << "\n...................." << std::endl;
}
int antoIncreaseF(std::vector<float>& src, float* dst, float step = 0.01)
{
for (size_t i = 0; i < src.size(); i++)
{
dst[i] = src[i] + step;
}
return 0;
}
int asrProcess(asrInOutput* asr)
{
for (size_t i = 0; i < 3; i++)
{
if (NULL == asr->inputF[i])
{
cout << "error: some input parameters of intput1,intput3,intput4 is null !!!" << endl;
return -1;
}
}
if (NULL == asr->inputI)
{
cout << "error: some input parameters,input2 is null !!!" << endl;
return -2;
}
printf("model data is %s \n", asr->modelName);
std::vector<float> input0(asr->inputF[0], asr->inputF[0] + asr->inputSize[0]);
std::vector<int> input1(asr->inputI, asr->inputI + asr->inputSize[1]);
std::vector<float> input2(asr->inputF[1], asr->inputF[1] + asr->inputSize[2]);
std::vector<float> input3(asr->inputF[2], asr->inputF[2] + asr->inputSize[3]);
//for (size_t i = 0; i < 4; i++)
//{
// std::cout << "sizePtr[" << i << "]==" << asr->inputSize[i] << std::endl;
//}
//printf("%s.size==%d \n", toStr(input0), input0.size());
//printf("%s.size==%d \n", toStr(input1), input1.size());
//printf("%s.size==%d \n", toStr(input2), input2.size());
//printf("%s.size==%d \n", toStr(input3), input3.size());
#ifdef F_OR_INT_PRINTT
printArrayData1(input0, (char*)toStr(input0));
printArrayData1(input2, (char*)toStr(input2));
printArrayData1(input3, (char*)toStr(input3));
#else
printArrayData1(input1, (char*)toStr(input1));
#endif
//set outsize buf
asr->outputFSize[0] = input0.size();
asr->outputFSize[1] = input2.size();
asr->outputFSize[2] = input3.size();
newOutputBuf(asr);
for (size_t i = 0; i < 3; i++)
{
std::cout << "asr->outputF[" << i << "]==" << asr->outputFSize[i] << std::endl;
}
antoIncreaseF(input0, asr->outputF[0],0.01);
antoIncreaseF(input2, asr->outputF[1],0.02);
antoIncreaseF(input3, asr->outputF[2],0.03);
return 0;
}
int newOutputBuf(asrInOutput* asr)
{
for (size_t i = 0; i < 3; i++)
{
if (asr->outputFSize[i] > asr->outBufNumberMax[i])
{
if (NULL != asr->outputF[i])
{
printf("delete[] asr->outputF[%d]\n", i);
delete[] asr->outputF[i];
}
asr->outBufNumberMax[i] = asr->outputFSize[i];
asr->outputF[i] = new float[asr->outBufNumberMax[i]];
printf("asr->outputF[%d]= new float[asr->outBufNumberMax[%d]\n", i,i);
}
}
return 0;
}
void asrRealeas(asrInOutput* asr)
{
for (size_t i = 0; i < 3; i++)
{
if (NULL != asr->outputF[i])
{
delete[] asr->outputF[i];
asr->outputF[i] = NULL;
}
}
}
int testInputOutput(asrInOutput* asr)
{
int outFSize[3] = { 5,6,8 };
//pptr[0] = asr->output1;
//pptr[1] = asr->output2;
//pptr[2] = asr->output3;
for (size_t i = 0; i < 3; i++)
{
asr->outputFSize[i] = outFSize[i];
//outputi buf
if (outFSize[i] > asr->outBufNumberMax[i])
{
if (NULL != asr->outputF[i])
{
printf(" pptr[%d]!=null \n", i);
delete[] asr->outputF[i];
}
else
{
printf(" pptr[%d]==null \n",i);
}
asr->outBufNumberMax[i] = outFSize[i];
asr->outputF[i] = new float[asr->outBufNumberMax[i]];
}
}
float ct=1.1;
for (size_t i = 0; i < 3; i++)
{
//std::cout << "line==" << __LINE__ <<"outFSize[i]="<< outFSize[i] << std::endl;
for (size_t j = 0; j < outFSize[i]; j++)
{
ct += 0.1;
asr->outputF[i][j] = ct;
}
printf("asr.outBufNumberMax[%d]==%d\n", i, asr->outBufNumberMax[i]);
//std::cout << "line==" <<__LINE__<< std::endl;
}
std::cout << "testInputOutput end" << std::endl;
return 0;
}
int testPrint()
{
std::cout << "c++ can be call,<*>||<*>" <<std::endl;
return 0;
}
2. python端调用代码
python是直接调用本地vs工程生成的dll库的,如果是linux上,要修改对应头文件和导出声明方式,生成对应so才可被调用,具体可自行查找,个人只是记录一个使用例子,在vs上方便调试所以才有这个例子的。python要特别注意数据类型,比如np.float32才对应c/c++端float,np.int32对应c/c++端int,如果使用默认类型可能不对。至于返回的ctype类型怎么变为numpy类型,则自行探索,最差的可以一个个的赋值。
import platform
import numpy as np
import ctypes
from ctypes import *
class AsrStruct(Structure):
_fields_ = [('modelName', c_char_p),
('inputF', (POINTER(c_float))*3),
('inputI', POINTER(c_int)),
('inputSize', (c_int * 4)),
('outputF', (POINTER(c_float))*3),
('outputFSize', (c_int*3)),
('outBufNumberMax', (c_int*3)),
]
def test2():
para = AsrStruct()
lib = CDLL(
'库路径/ConsoleApplication1/x64/Release/ConsoleApplication1.dll') # class level loading lib
# prepare input data
model_path = 'stream.mnn'.encode("utf-8")
input_sizes = np.zeros(5, dtype=np.int32)
input0 = np.arange(1, 5, 0.1, dtype=np.float32) #
input_sizes[0] = input0.size
input1 = np.arange(100, 120, 1, dtype=np.int32) #
input_sizes[1] = input1.size
input2 = np.arange(5, 15, 0.2, dtype=np.float32) #
input_sizes[2] = input2.size
input3 = np.arange(15, 30, 0.3, dtype=np.float32) #
input_sizes[3] = input3.size
para.modelName=c_char_p(model_path)
para.inputF[0] = input0.ctypes.data_as(POINTER(c_float))
para.inputI = input1.ctypes.data_as(POINTER(c_int))
para.inputF[1] = input2.ctypes.data_as(POINTER(c_float))
para.inputF[2] = input3.ctypes.data_as(POINTER(c_float))
for i in range(0,4):
para.inputSize[i]=input_sizes[i]
outputF=lib.asrProcess(byref(para))
for i in range(0, 3):
print('--------------------------------------repeate circle {}-----------------------------------'.format(i))
for k in range(0,3):
para.inputF[k]=para.outputF[k]
para.inputSize[0]=para.outputFSize[0]
para.inputSize[2]=para.outputFSize[1]
para.inputSize[3]=para.outputFSize[2]
outputF = lib.asrProcess(byref(para))
file_write = open('./1.txt', mode='w')
for i in range(0,para.outputFSize[0]):
a=para.outputF[0][i]
print(a)
file_write.write("%.5f\n" % a)
file_write.close()
def test():
lib = CDLL(
'库路径/ConsoleApplication1/x64/Release/ConsoleApplication1.dll') # class level loading lib
lib.testPrint.restype = c_int
lib.testPrint()
para = AsrStruct()
np1= np.arange(5, 15, 0.2, dtype=np.float32) #
para.input1=np1.ctypes.data_as(POINTER(c_float))
lib.testInputOutput.argtypes=[POINTER(AsrStruct)]
lib.testInputOutput.restype=c_int
c=lib.testInputOutput(byref(para))
print(para.outputFSize[0])
k1=para.outBufNumberMax
int_nparray = np.frombuffer(para.outBufNumberMax, dtype=np.int32)
print(int_nparray)
file_write = open('./1.txt', mode='w+')
for i in range(0,para.outputFSize[0]):
print(para.outputF[0][i])
file_write.writelines(para.outputF[0][i])
file_write.close()
#print(para[0].aa)
if __name__ == '__main__':
print("hello world")
print(platform.system())
#test()
test2()
备注:
可能例子过于简单,这仅仅做一个记录,但愿对有缘之人有帮助,免得像我一样浪费很多时间去瞎琢磨。一般不回信,时间长了会忘记的,瞄~