視頻教程: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é)果
