将std ::结构列表转换为Python列表

时间:2017-06-14 14:38:59

标签: python c++ swig

我正在研究用C ++编写的Python加速器模块。它是Dijkstra算法的一个实现,最终返回一个指向Route对象的指针,该对象又包含std::list<struct waypoint>。要使用Python与此对象进行交互,我编写了以下接口文件:

%{
#include "universe.hpp"
%}

%include <std_list.i>
%include "universe.hpp"

%template(Waypoints) std::list<struct waypoint>;

这允许我访问列表对象并对其进行索引,但是它上面的迭代器无法正常工作:

>>> r
<mod.Route; proxy of <Swig Object of type 'Route *' at 0x7f6ab39ff4e0> >
>>> r.points
<mod.Waypoints; proxy of <Swig Object of type 'std::list< waypoint > *' at 0x7f6ab502d630> >
>>> r.points[0]
<mod.waypoint; proxy of <Swig Object of type 'waypoint *' at 0x7f6ab39ff540> >
>>> for x in r.points:
...     print(x)
... 
<Swig Object of type 'unknown' at 0x7f6ab39ff5d0>
swig/python detected a memory leak of type 'unknown', no destructor found.
<Swig Object of type 'unknown' at 0x7f6ab39ff5a0>
swig/python detected a memory leak of type 'unknown', no destructor found.
<Swig Object of type 'unknown' at 0x7f6ab39ff5d0>
swig/python detected a memory leak of type 'unknown', no destructor found.
...

为了解决这个问题(并使代码更好读取)我想将std::list<struct waypoint>转换为普通的Python列表,所以我编写了以下类型映射:

%{
#include "universe.hpp"
%}

%typemap(out) std::list<struct waypoint> {
    $result = PyList_New($1->size());

    printf("This never gets printed\n");

    for(size_t i = 0; i < $1->size(); ++i) {
        PyList_SET_ITEM($result, i, $1[i]);
    }
}

%include <std_list.i>

%include "universe.hpp"

但是,这种类型图不起作用。事实上,该列表现在只是一个SwigPyObject,根本不支持订阅。由于永远不会执行其中的print语句,因此也永远不会执行typemap。我很难在网上找到有同样问题的人,我希望这里有人可以帮我弄清楚我做错了什么。

这些是universe.hpp的最小内容:

#include <string>
#include <list>

struct Entity;

struct waypoint {
    Entity *entity;
    int type;
};

class Route {
public:
    int loops;
    double cost;
    std::list<struct waypoint> points;
};

class Entity {
public:
    float x, y, z;
    int id, seq_id;
    std::string name;
};

1 个答案:

答案 0 :(得分:1)

这对我来说最初看起来像一个SWIG错误。它可能不是,最简单的解决方法是微不足道的改变线:

%template(Waypoints) std::list<struct waypoint>;

简单地说:

%template(Waypoints) std::list<waypoint>;

你所有的烦恼都将结束。这足以允许以下代码成功运行:

import test
r=test.Route()
r.points[0]

for x in r.points:
    print(x)

仅打印:

<test.waypoint; proxy of <Swig Object of type 'waypoint *' at 0x7fe439e22ed0> >
<test.waypoint; proxy of <Swig Object of type 'waypoint *' at 0x7fe439ea5360> >

为了完整起见,这是我认识到的旅程:

基本上似乎没有生成swig_type_info *swig::type_info<waypoint>()的特殊化,因此通过SwigPyIterator::next()的{​​{1}}调用最终会以SwigPyIterator::value()指针生成null } swig_type_info类型,当它调用waypoint生成Python代理对象时。这导致您稍后看到的未知类型信息。

与此同时,虽然有两种方法可以支持。首先在Python中,您仍然可以使用以下内容从C ++ SWIG_InternalNewPointerObj中轻松获取Python列表:

std::list

其次,我们可以添加自己的专业化,这很简单:

list(map(r.points.__getitem__, range(r.points.size())))

如果我们在调试器中进一步挖掘,但我们发现%{ #include "universe.hpp" // Workaround: add missing specialization namespace swig { // Forward declare first template <typename T> swig_type_info *type_info(); template <> swig_type_info *type_info<waypoint>() { return SWIGTYPE_p_waypoint; }; } %} %include <std_list.i> %include "universe.hpp" %template(Waypoints) std::list<struct waypoint>; 的默认实现实际上调用了type_info()。这反过来调用traits_info<Type>::type_info()。并且指向这里我们可以更清楚地看到问题是什么 - 我们构建的查询在其前面加上了“struct”这个词。为type_query()生成的swig_type_info结构在任何地方都没有结构,因此类型查询系统失败。

我有点惊讶的是,类型查询函数使用字符串执行此操作,即使在编译时已知类型 - 我上面使用的特殊化更简单,并且看起来很容易生成。但这并不是为什么它在这里不起作用,而真正的解决方案是从waypoint调用中删除struct这个词。