[英]Converting a std::list of structs to a Python list
我正在研究用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列表,所以我寫了下面的typemap:
%{
#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;
};
這對我來說最初看起來像一個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()
,所以SwigPyIterator::next()
調用,通過SwigPyIterator::value()
以swig_type_info
的null
指針結束當它調用SWIG_InternalNewPointerObj
來生成Python代理對象時的waypoint
類型。 這導致您稍后看到的未知類型信息。
與此同時,雖然有兩種方法可以支持。 首先在Python中,你仍然可以通過以下方式從C ++ std::list
輕松獲取Python列表:
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()
。 這反過來調用type_query()
。 並且指向這里我們可以更清楚地看到問題是什么 - 我們構建的查詢在其前面加上了“struct”這個詞。 為waypoint
生成的swig_type_info
結構在任何地方都沒有struct,因此類型查詢系統失敗。
我有點驚訝的是,類型查詢函數使用字符串執行此操作,即使在編譯時已知類型 - 我上面使用的特化更簡單,並且看起來很容易生成。 但這並不是為什么它在這里不起作用,真正的解決方案是從%template
調用中刪除單詞struct
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.