簡體   English   中英

將std ::結構列表轉換為Python列表

[英]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_infonull指針結束當它調用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM