C++和Python的數(shù)據(jù)類型的相互轉(zhuǎn)換

視頻教程:C++和Python的數(shù)據(jù)類型的相互轉(zhuǎn)換
Git: https://github.com/JasonLiThirty/C-andPython

我們都知道,Python本身呢是一門動態(tài)語言,變量的類型是在運行時才會被確定的,所以PythonCalc.py腳本中的Add方法呢不僅僅可以滿足int型數(shù)據(jù)相加,同樣也能支持其他類型的相加,既然是這樣,那在示例工程中的PyInvoker中就沒必要也不可能為每一種數(shù)據(jù)類型都建立一個函數(shù)來完成加法功能,所以這里就分享下使用C++的泛型函數(shù),泛型類和泛型特化來完成相應(yīng)功能。

在PyInvoker這個類里建立一個Add的泛型函數(shù),它使用泛型來支持多種數(shù)據(jù)類型的調(diào)用,同時支持將多種C++基礎(chǔ)數(shù)據(jù)類型轉(zhuǎn)換為Python數(shù)據(jù)類型,進而來調(diào)用PythonCalc.py腳本中的Add方法。

以下是自定義數(shù)據(jù)類型的相互轉(zhuǎn)化

在Common.h里來定義一個新的數(shù)據(jù)類型CustomType,它實際上是一個元素類型為std::string的std::list類型。

然后我們在建立了兩個CustomType對象One和Two,同時在這兩個對象里附上了一些值,將這兩個對象作為參數(shù)來調(diào)用PyInvoker的Add泛型函數(shù),期望的結(jié)果時能將兩個對象里面的鏈表合并并返回給C++。

在示例工程里建立了兩個頭文件,一個是ToPyConverter.h,在其中建立了一個泛型的ToPy的結(jié)構(gòu)體,用來將C++類型轉(zhuǎn)換為Python類型;另一個是ToCppConverter.h,在其中建立了一個泛型的ToCpp的結(jié)構(gòu)體,用來將Python類型轉(zhuǎn)換為C++類型。

我們先來看下泛型ToPy結(jié)構(gòu)體,它的內(nèi)部呢有一個boost::python::object成員變量obj,這個結(jié)構(gòu)體的作用呢就是將構(gòu)造函數(shù)里傳入的C++對象轉(zhuǎn)為為這個boost::python::object成員變量,同時結(jié)構(gòu)體重載了()運算符,從而能將轉(zhuǎn)換好的obj對象提供給Python使用。

對于基礎(chǔ)的數(shù)據(jù)類型,只需要通過構(gòu)造boost::python::object對象就可以完成C++數(shù)據(jù)類型到Python數(shù)據(jù)類型的轉(zhuǎn)換,但是對于向我們定義CustomType這種自定義類型就需要使用泛型特化來做一個特殊的處理。C++中的泛型特化(template specialization)就是針對泛型函數(shù)和泛型類中針對特定類型不能通用時所能采取的操作。

在ToPyConverter.h里針對CustomType建立了一個ToPy結(jié)構(gòu)體的泛型特化,用來處理CustomType類型的轉(zhuǎn)化,其實就是實現(xiàn)std::list到PythonList的轉(zhuǎn)化。

泛型ToCpp結(jié)構(gòu)體的內(nèi)部呢同樣有一個boost::python::object成員變量obj,用來存放構(gòu)造函數(shù)里傳入的Python類型的對象,結(jié)構(gòu)體重載了()運算符,用來實現(xiàn)Python類型的Obj對象到需要的C++類型對象。

同樣,對于基礎(chǔ)的數(shù)據(jù)類型,只需要通過boost::python::extract<T>就可以完成Python數(shù)據(jù)類型到C++數(shù)據(jù)類型的轉(zhuǎn)換,對于CustomType這種自定義類型同樣需要使用泛型特化來做特殊的處理,其實就是實現(xiàn)PythonList到std::list的轉(zhuǎn)化。

建立好ToPy和ToCpp泛型類和特化之后,我們來講PyInvoker里面的泛型函數(shù)Add進行下修改,將從C++類型對象轉(zhuǎn)為Python類型對象的參數(shù)修改為使用ToPy結(jié)構(gòu)體對象操作,將從Python類型對象轉(zhuǎn)為C++類型對象的返回值修改為使用ToCpp結(jié)構(gòu)體對象操作。


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函數(shù)

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;
}

輸出結(jié)果

2020-05-10_10-57-13.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容