【Pybind11 C++调用Python并返回结果】

在这里插入图片描述

网上有很多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进行输出!

### 使用 Pybind11 实现 C++ 调用 Python 为了实现 C++Python 的交互,Pybind11 提供了一个强大的框架来简化这一过程。通过 Pybind11 可以轻松地在 C++ 中嵌入执行 Python 代码。 #### 初始化 Python 解释器 要在 C++ 程序中调用 Python 代码,首先需要初始化 Python 解释器: ```cpp #include <pybind11/embed.h> // everything needed for embedding namespace py = pybind11; int main() { py::scoped_interpreter guard{}; // Start the interpreter and keep it alive // ... rest of your program ... } ``` 这段代码会启动 Python 解析器,保持其运行直到程序结束[^1]。 #### 执行简单的 Python 表达式 一旦解释器被成功初始化,就可以开始执行一些基本的 Python 命令或表达式: ```cpp py::print("Hello, World!"); // Prints "Hello, World!" from within C++ auto result = py::eval("6 * 7"); // Evaluates a simple expression std::cout << "The answer is " << result.cast<int>() << std::endl; ``` 这里展示了如何打印消息以及评估简单算术运算的结果。 #### 导入模块和函数 除了直接编写 Python 语句外,还可以导入现有的 Python 模块及其功能: ```cpp try { auto sys = py::module_::import("sys"); sys.attr("path").attr("append")("."); auto my_module = py::module_::import("my_module"); int sum_result = my_module.attr("add")(3, 4).cast<int>(); } catch (const py::error_already_set& e) { PyErr_Print(); } ``` 上述例子说明了怎样安全地处理可能发生的错误,且动态加载外部定义的功能。 #### 定义回调机制 有时希望从 Python 函数接收返回值或将数据传递给它;这可以通过定义适当的封装类来完成: ```cpp class CallbackWrapper : public py::wrapper<CallbackInterface> { public: void callback_method(int value) override { PYBIND11_OVERRIDE_PURE( void, CallbackInterface, callback_method, value); } }; // Registering the wrapper with pybind11... PYBIND11_MODULE(example_extension, m) { py::class_<CallbackInterface>(m, "CallbackInterface") .def(py::init<>()) .def("callback_method", &CallbackInterface::callback_method); py::class_<CallbackWrapper, CallbackInterface>(m, "CallbackWrapper") .def(py::init<>()); } // Using the wrapped class in Python side... from example_extension import CallbackWrapper as CW cw_instance = CW() result = cw_instance.callback_method(42) ``` 此部分介绍了更复杂的场景——即当涉及到双向通信时应采取的方法。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值