网上有很多CPP用Pybind11生成pyd的的代码,很少有CPP直接调用python的(想看py调用CPP的看https://www.jianshu.com/p/9619f8f02891,归纳的很全了)
这里简单介绍一下
首先,如果系统已经有最新的默认python3x64,那么直接使用这个的dll,lib以及环境变量,如果没有设置环境变量,那么自己百度里谷歌一下,因为这好像是一个bug,调用py的dll会搜索python的环境变量;如果没有安装,那么更好,直接下载python3.x 64bit,具体版本可以看Pybind11支持的最高版本,并且设置环境变量(一般安装python会有设置环境变量的勾选)
PS:32bit的自己想办法,什么年代了还用这个呢
然后Pybind11是支持C++11的,我们用VS2019+Win10x64开发
这里其实用python原生的C++api也可以,但是麻烦一点,底层应该都是一样
Pybind11已经帮我们做好了
show me the code!
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
#include <pybind11/embed.h> // everything needed for embedding
#include <iostream>
namespace py = pybind11;
using namespace std;
//C:\Users\23607\AppData\Local\Programs\Python\Python37\
//https://blog.csdn.net/mvp_Dawn/article/details/102535495
/*python3启动失败 Fatal Python error : initfsencoding: unable to load the file system codec*/
//使用主环境默认版本python和其环境变量以及dll
//Python 3.7.6 (default, Jan 8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32
int main() {
py::scoped_interpreter python;
py::module sys = py::module::import("sys");
py::print(sys.attr("path"));
py::module t = py::module::import("tttt");
py::object result;
try
{
result = t.attr("add")(1, 2);
}
catch (std::exception& e)
{
cout << "call python transpose failed:" << e.what() << endl;
}
auto outArray = result.cast<int>();
printf("outArray:%d\n", outArray);
/*
//返回值类型python代码
//tttt.py
def add(i, j):
print("hello,pybind11")
return i + j
*/
//返回数组测试,参考:https://blog.csdn.net/u013701860/article/details/109812323
//py::array_t<float> outArray = result.cast<py::array_t<float>>();
copy output data
//py::buffer_info outBuf = outArray.request();
//float* optr = (float*)outBuf.ptr;
//float* pOutData=new float[outArray.size()];
//memcpy(pOutData, optr, outArray.size() * sizeof(float));
//delete[]pOutData;
//返回数组python代码
/*
//python math_test.py
def transpose(data, perm):
import numpy as np
result = np.transpose(data, perm)
resultn = result.reshape(-1).reshape(result.shape)
return resultn
*/
getchar();
return 0;
}
接下来是喜闻乐见的字符串传递,跨平台的字符串传递是最头疼的事情,懂的都懂,反正就是反复套娃
python和cpp中文字符串传递:
注意,虽然t.attr(“xxx”)这个函数指针我们可以传入std::string(虽然在vc里这个是支持unicode16扩展编码字符的,包括中文,但是跨平台就不一定了)类型,但是对于std::wstring好像没有办法了,如果有,请告诉我,这里仅作抛砖引玉
我们的思路是过程中全部编码为utf-8风格的unsigned char*(->uint8),传递用int*和numpy
PS:既然已经用了uint8,那么其实我们还可以传递文件类型了,比如图片转化为rgb 的uint8后feed到python的opencv或者tensorflow的tensor等等;
py:
import numpy as np
def ToBytes(data):
if type(data) == type('12'):
if len(data)%2 != 0:
data += '0'
# print("add '0' at end,amended: ",end="")
# print(data)
return bytes().fromhex(data)
elif type(data) == type([1,]):
return bytes(data)
else:
print("only 'str' or 'list' is valid!")
return None
def process_str(int_np_array):
arr2 = list(int_np_array)
bytes_in=ToBytes(arr2)
_str=bytes_in.decode('utf-8')
print("input str:",_str)
_str="now process:"+_str
bytes = _str.encode(encoding='utf-8', errors = 'strict')
b=[a for a in bytes]
return b
CPP
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
#include <pybind11/embed.h> // everything needed for embedding
#include <iostream>
#include <windows.h>
//#include "base64.hpp"
namespace py = pybind11;
using namespace std;
//https://blog.csdn.net/wyansai/article/details/50764315
// 当type为CP_ACP时,GBK转化为UNICODE;当type为CP_UTF8时,UTF8转化为UNICODE
wchar_t* trans(const char* ch, int type = 0) //UNICODE==65001,CP_ACP==0
{
int len = MultiByteToWideChar(type, 0, ch, -1, nullptr, 0);
wchar_t* str = new wchar_t[len + 1];
wmemset(str, 0, len + 1);
MultiByteToWideChar(type, 0, ch, -1, str, len);
return str;
}
// 当type为CP_ACP时,UNICODE转化为GBK;当type为CP_UTF8时,UNICODE转化为UTF8
char* trans(const wchar_t* wch, int type = 0) {
int len = WideCharToMultiByte(type, 0, wch, -1, nullptr, 0, nullptr, nullptr);
char* str = new char[len + 1];
memset(str, 0, len + 1);
WideCharToMultiByte(type, 0, wch, -1, str, len, nullptr, nullptr);
return str;
}
//C:\Users\23607\AppData\Local\Programs\Python\Python37\
//https://blog.csdn.net/mvp_Dawn/article/details/102535495
/*python3启动失败 Fatal Python error : initfsencoding: unable to load the file system codec*/
//使用主环境默认版本python和其环境变量以及dll
//Python 3.7.6 (default, Jan 8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32
int main() {
py::scoped_interpreter python;
py::module sys = py::module::import("sys");
py::print(sys.attr("path"));
py::module t = py::module::import("imouto");
py::object result;
try
{
auto func = t.attr("process_str");
string inStr = "imouto is love";//"妹妹就是爱love";
auto xxb = trans(L"imouto is love爱", 65001);
auto len = strlen(xxb);//inStr.size();
const unsigned char* ss = (const unsigned char*)xxb;//(const unsigned char*) (inStr.c_str());
int* ss_copy = new int[len+1];
std::copy(ss,ss + len, ss_copy);
//for (size_t i = 0; i < len; i++)
//{
// printf("%d ", ss_copy[i]);
//}
//printf("\n");
ss_copy[len] = (int)'\0';
vector<int> inDataShape = { (int)(len + 1) };
// construct numpy array
py::array_t<int> npInputArray(inDataShape, ss_copy);
result = func(npInputArray);
delete[]ss_copy;
}
catch (std::exception& e)
{
cout << "call python transpose failed:" << e.what() << endl;
}
//返回数组测试,参考:https://blog.csdn.net/u013701860/article/details/109812323
py::array_t<int> outArray = result.cast<py::array_t<int>>();
py::buffer_info outBuf = outArray.request();
int* optr = (int*)outBuf.ptr;
unsigned char* pOutData = new unsigned char[outArray.size()];
//memcpy(pOutData, optr, outArray.size() * sizeof(int));
std::copy(optr, optr+ outArray.size(), pOutData);
for (size_t i = 0; i < outArray.size(); ++i)
{
printf("%d ", pOutData[i]);
}
//https://blog.csdn.net/CHYabc123456hh/article/details/109021250
string strHH;
strHH.append(reinterpret_cast<const char*>(pOutData));
auto xxx = trans(strHH.c_str(), 65001);
auto xxc = trans(xxx);
//std::wcout.imbue(std::locale("chs"));
//wcout << xxx << endl;
cout <<"\nresult:"<< xxc << endl;
delete[]pOutData;
//返回数组python代码
/*
//python math_test.py
def transpose(data, perm):
import numpy as np
result = np.transpose(data, perm)
resultn = result.reshape(-1).reshape(result.shape)
return resultn
*/
printf("--------end-----------\n");
getchar();
return 0;
}
如果没有意外的话传输结果是这样的:
这里我们cpp端传入一个中文字符串L"imouto is love爱"
python端接收后,处理一下马上返回给cpp端,cpp端解码到中文gbk std::string进行输出!