如何将SWIG类型映射应用于双指针结构参数
我有一个API,我试图使用SWIG包装,以便我可以从python调用底层的C库。
我遇到了一个特定的API fn:
int update_tracks(track_t **phash_tracks, const pdws_t *pdw_frame, const rdws_t *rdw_frame, lib_t *lib, lib_meta_t *lib_meta, const cfg_t *cfg);
它是track_t
数据结构的双指针,我无法处理。
所有单指针都可以正常工作。
这是唯一具有指向track_t
的双指针的API fn
所有其他只有一个指针,例如
void print_hash_tracks(const track_t *hash_tracks, const cfg_t *cfg, enum TRKTYPE trktype);
我很确定我需要在SWIG界面文件(interface.i)中创建一个类型图,但我发现SWIG文档难以理解。
我认为我需要做的是创建一个类型图,每当它看到track_t**
类型时,它需要一个track_t*
并将其转换为它的地址,如:
/* provide typemap to handle instances of track_t** parameters */ %typemap(in) track_t** (track_t *tracks) { $1 = &tracks; }
但是我跑步时只是出现了分段错误:
tracks = g3.track_t() g3.update_tracks(tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
在python方面。
我觉得我几乎已经解决了这个问题,但是不能正确地获得类型图规范,同时也在努力理解相关文档。
柔印 – 如果你在那里 – 也许你可以对此有所了解,你似乎是这方面的SO专家..
更新 – m7ython(太棒了!SO的另一位SWIG专家)
C中的用法非常简单
将track_t指针声明并初始化为NULL:
track_t *hash_tracks = NULL;
然后:
update_tracks(&hash_tracks, &pdw_frame, &rdw_frame, &lib, &lib_meta, &cfg);
因此,指向track_t
的指针的地址作为arg传递给update_tracks()
。 update_tracks()
fn负责处理放入hash_tracks的数据的所有必要hash_tracks
,即track_t
结构的哈希表
所有其他args都是单指针,我可以在python端创建并填充它们没有任何问题。
track_t
是一个包含一堆int,float,char *等的结构
typedef struct { /* make struct hashable */ UT_hash_handle hh; int id; ... char name[MAX_BUF]; ... } track_t;
track_t arg是track_t**
而不仅仅是track_t*
的原因是hash_tracks
是指向哈希表的指针(使用UTHash库)。 hash_tracks
指向哈希表中的第一个track_t。 在update_tracks()
的主体中,可以在散列表中添加/删除fn track_t结构,以便指向第一个track_t的指针可能会发生变化,即hash_tracks
在调用update_tracks()
之后可能指向其他内容,因此原因是用于将指针传递给指针。
换句话说, track_t**
arg, phash_tracks
被用作输入和输出类型arg,因此指向指针。 所有其他args只是输入,它们不会改变,所以它们可以作为单个指针传入。
我尝试使用以下C fn的’helper fn’路线:
track_t** make_phash_tracks(void) { track_t **phash_tracks; phash_tracks = calloc(1, sizeof(track_t*)); return phash_tracks; }
使用calloc应该确保* phash_tracks为NULL
这个编译和包装没有任何错误,但是当我从python一侧使用它时,它会被分段,例如
phash_tracks = g3.make_phash_tracks() g3.update_tracks(phash_tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
在调用update_tracks
之前检查phash_tracks
var给出了:
(Pdb) p phash_tracks
编辑:好的,我想我现在明白了update_tracks
作用。 看来你可以用两种方式使用这个function。 要么更新现有tracks
,要么在将指针传递给NULL
指针时创建tracks
。 我不确定在SWIG中处理这两种情况的最优雅的方法(或者如果这甚至是一个问题),但这里有一些选择。
1. phash_tracks
是一个输出参数
首先,您必须将*phash_tracks
作为返回值传递回Python,并以某种forms使用该函数
>>> int_res, tracks = g3.update_tracks(tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
要么
>>> int_res, tracks = g3.update_tracks(pdw_frame, rdw_frame, lib, lib_meta, cfg)
这是通过以下“argout”类型映射完成的:
%typemap(argout) track_t **phash_tracks { %append_output(SWIG_NewPointerObj(%as_voidptr(*$1), $*1_descriptor, SWIG_POINTER_OWN)); }
也许你不希望Python取得track_t*
所有权,然后将SWIG_POINTER_OWN
替换为0
。
2.传递空的phash_tracks
如果您只想使用update_tracks
函数创建 tracks
,您可以基本上完成您已经在做的事情。 使用以下“in”类型映射,并使用上面第二个示例中的函数(不带tracks
参数)。
%typemap(in, numinputs=0) track_t **phash_tracks (track_t *tracks) { tracks = NULL; $1 = &tracks; }
3. phash_tracks
作为输入(和输出)参数
如果要使用update_tracks
更新现有 tracks
,您应该能够使用我之前建议的“in”类型映射,并使用Python中的函数,如第一个示例(包括tracks
参数)。
%typemap(in) track_t **phash_tracks (track_t *tracks) { if ((SWIG_ConvertPtr($input, (void **) &tracks, $*1_descriptor, SWIG_POINTER_EXCEPTION | SWIG_POINTER_DISOWN)) == -1) return NULL; $1 = &tracks; }
请注意,Python tracks_t*
其tracks_t*
非常重要。
4.启用上面的(2)和(3)
您基本上可以使用版本(3)来创建 tracks
,如果您可以通过swig传递包装的NULL
tracks_t*
。 我不确定SWIG是否允许这样做 – 但也许确实如此。 尝试使用辅助函数:
tracks_t* empty_tracks() { return NULL; }
或者,您可以沿着以下行修改“in”类型映射,尝试将提供的参数转换为track_t*
并传递其地址,或者传递NULL
track_t*
的地址。
%typemap(in) track_t **phash_tracks (track_t *tracks) { // Alternatively, check if $input is a 0 integer `PyObject`... if ((SWIG_ConvertPtr($input, (void **) &tracks, $*1_descriptor, SWIG_POINTER_DISOWN)) == -1) tracks = NULL; $1 = &tracks; }
然后,从Python中,只需传递其他东西来创建tracks
:
>>> int_res, tracks = g3.update_tracks(0, pdw_frame, rdw_frame, lib, lib_meta, cfg)