C++和Python的数据类型的相互转换

视频教程:C++和Python的数据类型的相互转换
Git: https://github.com/JasonLiThirty/C-andPython

我们都知道,Python本身呢是一门动态语言,变量的类型是在运行时才会被确定的,所以PythonCalc.py脚本中的Add方法呢不仅仅可以满足int型数据相加,同样也能支持其他类型的相加,既然是这样,那在示例工程中的PyInvoker中就没必要也不可能为每一种数据类型都建立一个函数来完成加法功能,所以这里就分享下使用C++的泛型函数,泛型类和泛型特化来完成相应功能。

在PyInvoker这个类里建立一个Add的泛型函数,它使用泛型来支持多种数据类型的调用,同时支持将多种C++基础数据类型转换为Python数据类型,进而来调用PythonCalc.py脚本中的Add方法。

以下是自定义数据类型的相互转化

在Common.h里来定义一个新的数据类型CustomType,它实际上是一个元素类型为std::string的std::list类型。

然后我们在建立了两个CustomType对象One和Two,同时在这两个对象里附上了一些值,将这两个对象作为参数来调用PyInvoker的Add泛型函数,期望的结果时能将两个对象里面的链表合并并返回给C++。

在示例工程里建立了两个头文件,一个是ToPyConverter.h,在其中建立了一个泛型的ToPy的结构体,用来将C++类型转换为Python类型;另一个是ToCppConverter.h,在其中建立了一个泛型的ToCpp的结构体,用来将Python类型转换为C++类型。

我们先来看下泛型ToPy结构体,它的内部呢有一个boost::python::object成员变量obj,这个结构体的作用呢就是将构造函数里传入的C++对象转为为这个boost::python::object成员变量,同时结构体重载了()运算符,从而能将转换好的obj对象提供给Python使用。

对于基础的数据类型,只需要通过构造boost::python::object对象就可以完成C++数据类型到Python数据类型的转换,但是对于向我们定义CustomType这种自定义类型就需要使用泛型特化来做一个特殊的处理。C++中的泛型特化(template specialization)就是针对泛型函数和泛型类中针对特定类型不能通用时所能采取的操作。

在ToPyConverter.h里针对CustomType建立了一个ToPy结构体的泛型特化,用来处理CustomType类型的转化,其实就是实现std::list到PythonList的转化。

泛型ToCpp结构体的内部呢同样有一个boost::python::object成员变量obj,用来存放构造函数里传入的Python类型的对象,结构体重载了()运算符,用来实现Python类型的Obj对象到需要的C++类型对象。

同样,对于基础的数据类型,只需要通过boost::python::extract<T>就可以完成Python数据类型到C++数据类型的转换,对于CustomType这种自定义类型同样需要使用泛型特化来做特殊的处理,其实就是实现PythonList到std::list的转化。

建立好ToPy和ToCpp泛型类和特化之后,我们来讲PyInvoker里面的泛型函数Add进行下修改,将从C++类型对象转为Python类型对象的参数修改为使用ToPy结构体对象操作,将从Python类型对象转为C++类型对象的返回值修改为使用ToCpp结构体对象操作。


Common.h



#ifndef PY_COMMON_H
#define PY_COMMON_H

#define PYTHON_IMPORT_SYS           "import sys\n"
#define PYTHON_IMPORT_DIR_PREF      "sys.path.append('"
#define PYTHON_IMPORT_DIR_SUF       "')\n"

using CustomType = std::list<std::string>;

#endif

PyInvoker.h

#ifndef PY_INVOKER_H
#define PY_INVOKER_H

#define BOOST_PYTHON_STATIC_LIB

#include <boost/python.hpp>
#include <python.h>
#include <vector>
#include <string>
#include <list>
#include "ToPyConverter.h"
#include "ToCppConverter.h"

class _declspec(dllexport) PyInvoker
{
public:
    PyInvoker() {};
    ~PyInvoker() {};

    static bool Initialize();
    static void Finalize();
    static void ImportModule(std::string path);
    static void Trail();
    static void RunFunc(std::string module, std::string func);
    static int RunParasFunc(std::string module, std::string func, std::vector<int> paras);

    template<class T>
    static T Add(std::string module, std::string func, std::vector<T> paras);
};
template<class T>
T PyInvoker::Add(std::string module, std::string func, std::vector<T> paras)
{
    PyLock lock;
    T result;
    try
    {
        boost::python::object pyModule(boost::python::import(module.c_str()));
        boost::python::object paramOne = ToPy<T>(paras[0]);
        boost::python::object paramTwo = ToPy<T>(paras[1]);
        boost::python::object pyResult(pyModule.attr(func.c_str())(paramOne, paramTwo));

        result = ToCpp<T>(pyResult);
    }
    catch (...)
    {
        PyErr_Print();
        PyErr_Clear();
    }
    return result;
}

class _declspec(dllexport) PyLock
{
public:
    PyLock()
    {
        m_state = PyGILState_Ensure();
    }

    ~PyLock()
    {
        PyGILState_Release(m_state);
    }
private:
    PyGILState_STATE m_state;
};

#endif //PY_INVOKER_H

ToPyConverter.h

#ifndef TO_PY_CONVERTER_H
#define TO_PY_CONVERTER_H

#include "Common.h"
#include <boost/python.hpp>
#include <list>
#include <string>

template<class T>
struct ToPy
{
    boost::python::object obj;

    ToPy(T data)
    {
        obj = boost::python::object(data);
    }

    operator boost::python::object()
    {
        return obj;
    }
};

template<>
struct ToPy<CustomType>
{
    boost::python::object obj;

    ToPy(CustomType data)
    {
        boost::python::list pyList;
        for (CustomType::iterator iter = data.begin(); iter != data.end(); ++iter)
        {   
            pyList.append(boost::python::object(*iter));
        }
        obj = pyList;
    }

    operator boost::python::object()
    {
        return obj;
    }
};



#endif

ToCppConverter.h

#ifndef TO_CPP_CONVERTER_H
#define TO_CPP_CONVERTER_H

#include "Common.h"
#include <boost/python.hpp>
#include <list>
#include <string>

template<class T>
struct ToCpp
{
    boost::python::object obj;
    ToCpp(boost::python::object data) :obj(data)
    {
        ;
    }

    operator T()
    {
        return boost::python::extract<T>(obj);
    }
};

template<>
struct ToCpp<CustomType>
{
    boost::python::object obj;
    ToCpp(boost::python::object data) :obj(data)
    {
        ;
    }

    operator CustomType()
    {
        CustomType cppList;
        boost::python::list pyList = boost::python::extract<boost::python::list>(obj);
        for (int i = 0; i < boost::python::len(pyList); i++)
        {
            std::string value = boost::python::extract<std::string>(pyList[i]);
            cppList.push_back(value);
        }
        return cppList;
    }
};

#endif

main函数

int main()
{
    if (!PyInvoker::Initialize())
    {
        std::cout << "----Python Enviorment Initialized Failed!----" << std::endl;
        return -1;
    }

    //std::cout << "----Trail----" << std::endl;
    //PyInvoker::Trail();

    PyInvoker::ImportModule(GetCurrentDir() + "pyscripts");

    //PyInvoker::RunFunc("PythonGreet", "Hello");



    //std::vector<int> parasInt;
    //parasInt.push_back(3);
    //parasInt.push_back(4);
    //std::cout << "Result = " << PyInvoker::RunParasFunc("PythonCalc", "Add", parasInt) << std::endl;



    std::vector<int> parasInt;
    parasInt.push_back(3);
    parasInt.push_back(4);
    std::cout << "Result = " << PyInvoker::Add<int>("PythonCalc", "Add", parasInt) << std::endl;

    std::vector<float> parasFloat;
    parasFloat.push_back(2.7);
    parasFloat.push_back(3.1);
    std::cout << "Result = " << PyInvoker::Add<float>("PythonCalc", "Add", parasFloat) << std::endl;

    std::vector<std::string> parasString;
    parasString.push_back(std::string("Hello "));
    parasString.push_back(std::string("world!"));
    std::cout << "Result = " << PyInvoker::Add<std::string>("PythonCalc", "Add", parasString).c_str() << std::endl;

    
    std::vector<CustomType> parasList;
    CustomType One;
    One.push_back(std::string("The "));
    One.push_back(std::string("World "));
    CustomType Two;
    Two.push_back(std::string("is "));
    Two.push_back(std::string("Font!"));
    parasList.push_back(One);
    parasList.push_back(Two);
    CustomType resultList = PyInvoker::Add<CustomType>("PythonCalc", "Add", parasList);
    
    std::cout << "list after merged:" << std::endl;
    for (CustomType::iterator iter = resultList.begin(); iter != resultList.end(); ++iter)
    {
        std::cout << (*iter).c_str() << std::endl;
    }
    
    PyInvoker::Finalize();
    return 0;
}

输出结果

2020-05-10_10-57-13.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349