视频教程: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;
}