前言
python和c++之间数据类型的差别是进行转换时经常碰到的问题,有些简单的数据类型可以通用,但还有很多中类型则无法直接使用。在碰到这类问题时,boost.python提供的extract类是一个很方便的解决方案。这篇内容用于记录我在使用中对extract的理解。
1. 说明
1.1 Extract介绍
Extract是Boost.Python中用来从Python对象中提取c++对象的类,通过这个类我们可以将一些Python的对象转换成C++语言的对象。如将 boost::python::str
类型的变量转换成char *
型变量。
Extract 的执行成员必须是一个非重载静态函数,其单个参数是 Python 对象类型。可接受的 Python 对象类型包括从 PyObject 派生的那些公开(且明确)的对象类型,以及与 PyObject 布局兼容的 POD 类型。
注:要想从python对象中提取c++类型,则该对象首先要包含这种类型的内容。
1.2 使用方法
这里介绍几种基本的使用方法。
1. 获取python对象的值
int a = boost::python::extract<int>(x)
从python对象x中提取int的内容。
2. 提取前使用构造器检查
int g(object x)
{
extract<int> get_int(x);
if (get_int.check())
return get_int();
else
return 0;
}
构造器get_int先调用check()
来检查是否能将x转换成int类型的数据。
3. 使用extract直接修改可变对象
我们可以使用extract在不拷贝目标对象的情况下,直接方便地修改boost::python::object
及其衍生类型的对象或PyObject*。
namespace bp = boost::python;
// bp::object x;
dict d = bp::extract<bp::dict>(x.attr("__dict__"));
d["whatever"] = 3; // modifies x.__dict__ !
// PyObject* pyobj;
PyObject* __dict__ = PyObject_GetAttrString(pyobj, const_cast<char*>("__dict__"));
// (see also http://article.gmane.org/gmane.comp.python.c%2B%2B/15664)
bp::dict dictobj = bp::extract<bp::dict>(__dict__);
dictobj["whatever"] = 3; // modifies pyobj __dict___!
4. 从更复杂的结构中提取元素
为了从列表和元组等复杂结构中提取元素,我们必须嵌套提取调用。这里假设有一个元素类型为tuple的list变量,从中提取出tuple内的数据。
boost::python::list l1 = [(1, 2), (3, 4), (5, 6)];
extract<int>(extract<tuple>(list[0])());
2. 示例
为了演示,在之前的RealWorld的类中添加一个ShowRelatives()函数,该函数将传进来的python list类型数据转换成c++下的std::vector
。
声明和定义函数
在 classes.hpp 的RealWorld类下添加函数声明:
void ShowRelatives(boost::python::list &rels);
其定义如下:
void RealWorld::ShowRelatives(python::list &rels)
{
std::vector<char *> rel(len(rels), 0);
for (int i = 0; i < len(rels); i++){
rel[i] = python::extract<char *> (rels[i]);
}
for (int i = 0; i < rel.size(); i++)
std::cout << rel[i] << " ";
std::cout << std::endl;
}
添加进python模块
.def ("ShowRelatives", &RealWorld::ShowRelatives, python::args("rels"))
简单测试
python测试程序如下:
#!/usr/bin/env python
import classes
t1 = classes.RealWorld("Xiangdi", 'm')
relative = ("father", "mother", "brother", "sister")
t1.ShowRelatives(relative)
运行结果
注:python list似乎不能直接转换成std::vectorstd::string的形式,因为我这么尝试的时候始终会报错:
Traceback (most recent call last):
File "classes.py", line 9, in <module>
t1.ShowRelatives(relative)
RuntimeError: basic_string::_M_construct null not valid
3. extract类详解
extract类定义在 <boost/python/extract.hpp> 文件中。
3.1 extract定义
template <class T>
struct extract
: converter::select_extract<T>::type
{
private:
typedef typename converter::select_extract<T>::type base;
public:
typedef typename base::result_type result_type;
operator result_type() const
{
return (*this)();
}
extract(PyObject*);
extract(api::object const&);
};
其中T表示要提取的目标c++类型。支持两种用法:
- extract(o) :产生一个隐式转换为 T 的临时对象(也可以通过对象的函数调用运算符进行显式转换)。但是,如果o中没有能够转换成T类型的对象,则会引发 Python TypeError 异常。
- extract x(o):这种方法构造一个提取器,该提取器内的 check() 成员函数会询问转换是否可用,而不会引发异常。
3.2 构造函数
extract(PyObject*);
extract(api::object const&);
在构造extract实例时,可以传入两种参数,一种是Python对象的指针,另一种是对象的引用。从两个函数的实现来看,第二种方法其实也是在函数体内部将引用对象的指针传递给父类对象。
3.3 成员函数
这里只介绍了一部分成员函数。
operator result_type() const
result_type()
函数作用是将存储的python对象的指针转换为T或T&类型,也就是我们在实例化extractor后会自动运行的动作。如果运行出错(比如python对象不包含T类型的内容),函数会触发TypeError。
从上面的定义代码中看不出来,extract的父类还提供了一些额外的成员函数,比如check()
。
bool check() const;
这个函数在extract的构造器中调用,以检查python对象能否转换成T或T&类型,如果不能返回false。需要注意的是即使不能转换也不会触发python异常。
参考资料
boost:Extractor
Chapter 5. To/From Python Type Conversion
boost.python/extract