我正在研究用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;
};
答案 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
这个词。