用boost.python封装结构体列表
我有一个C++的函数,它返回一个结构体的列表。在这个结构体里面,还有更多结构体的列表。
struct CameraInfo {
CamName name;
std::list<CamImageFormat> lImgFormats;
std::list<CamControls> lCamControls;
};
std::list<CameraInfo> getCameraInfo()
{
std::list<CameraInfo> lCamerasInfo;
// fill lCamerasInfo
return lCamerasInfo;
}
然后为了导出这些数据,我使用了:
class_<CameraNode....> >("CameraNode", no_init)
...
...
.def("listCameraInfo", make_function(&CameraNode::listCameraInfo))
.staticmethod("listCameraInfo")
...
;
这没问题,因为我用cout把数据打印到屏幕上……现在我想用Python来像使用属性一样使用这个返回值及其内容,像这样:
cameras = []
cameras = CameraNode.getCameraInfo()
print cameras[0].name
print cameras[0].lImgFormats[0]
and so on...
这可能吗??我应该使用add_property吗?我觉得我不能为每个结构体都创建一个类。这个设计在我只用C++的时候是合理的,但现在我需要把它封装起来,我越来越困惑了。
如果有人能给我一些关于如何用boost.python以一般方式封装std::list的建议,我会非常感激。
编辑:
我在这里添加一些我觉得有用的链接:
迭代器 Stl容器2 个回答
如果你只需要单向转换(从C++到Python),那么你可以直接定义一个转换器,从 list<list<YourClass> >
转换。你可以参考我的 vector<vector<string> > 转换器,只需根据需要更改类型,别忘了 注册这个转换器。
你也可以创建一个方法,返回 python::list
(这个列表里会包含你的对象的 python::list
),这个方法会遍历C++的嵌套列表,并把它转换成原生的Python列表,但这种方法只适用于你有的特定情况。
如果需要双向转换,可以看看我的文件(里面有不同类型的双向转换器)——优点是你可以得到原生的Python列表,缺点是需要复制对象。对于大型集合的双向转换,使用 indexing_suite
绝对是个好选择。
还有一个 indexing_suite_v2
,据说要好得多,直接支持 std::list
和 std::map
,不过可惜的是文档写得不太好(我上次查看是在大约1.5年前),而且它并不是 boost::python
的官方部分。
一定要用 std::list
吗?如果你用 std::vector
代替的话,可以使用 boost::python::vector_indexing_suite
来包装这个列表。想了解更多细节,可以看看 这篇文章。
如果你必须使用 std::list
,那么你需要创建一个辅助类,把 std::list
的功能用 Python 的 list
方法来封装。这可能会比较复杂,但也是可以做到的。
std_item.hpp:
#include <list>
#include <algorithm>
#include <boost/python.hpp>
template<class T>
struct listwrap
{
typedef typename T::value_type value_type;
typedef typename T::iterator iter_type;
static void add(T & x, value_type const& v)
{
x.push_back(v);
}
static bool in(T const& x, value_type const& v)
{
return std::find(x.begin(), x.end(), v) != x.end();
}
static int index(T const& x, value_type const& v)
{
int i = 0;
for(T::const_iterator it=x.begin(); it!=x.end(); ++it,++i)
if( *it == v ) return i;
PyErr_SetString(PyExc_ValueError, "Value not in the list");
throw boost::python::error_already_set();
}
static void del(T& x, int i)
{
if( i<0 )
i += x.size();
iter_type it = x.begin();
for (int pos = 0; pos < i; ++pos)
++it;
if( i >= 0 && i < (int)x.size() ) {
x.erase(it);
} else {
PyErr_SetString(PyExc_IndexError, "Index out of range");
boost::python::throw_error_already_set();
}
}
static value_type& get(T& x, int i)
{
if( i < 0 )
i += x.size();
if( i >= 0 && i < (int)x.size() ) {
iter_type it = x.begin();
for(int pos = 0; pos < i; ++pos)
++it;
return *it;
} else {
PyErr_SetString(PyExc_IndexError, "Index out of range");
throw boost::python::error_already_set();
}
}
static void set(T& x, int i, value_type const& v)
{
if( i < 0 )
i += x.size();
if( i >= 0 && i < (int)x.size() ) {
iter_type it = x.begin();
for(int pos = 0; pos < i; ++pos)
++it;
*it = v;
} else {
PyErr_SetString(PyExc_IndexError, "Index out of range");
boost::python::throw_error_already_set();
}
}
};
template<class T>
void export_STLList(const char* typeName)
{
using namespace boost::python;
class_<std::list<T> >(typeName)
.def("__len__", &std::list<T>::size)
.def("clear", &std::list<T>::clear)
.def("append", &listwrap<T>::add,
with_custodian_and_ward<1,2>()) // to let container keep value
.def("__getitem__", &listwrap<T>::get,
return_value_policy<copy_non_const_reference>())
.def("__setitem__", &listwrap<T>::set,
with_custodian_and_ward<1,2>()) // to let container keep value
.def("__delitem__", &listwrap<T>::del)
.def("__contains__", &listwrap<T>::in)
.def("__iter__", iterator<std::list<T> >())
.def("index", &listwrap<T>::index);
}
用法:
typedef std::list<int> intlist;
export_STLList<int>("intlist");