如何将SWIG类型映射应用于双指针结构参数

bph*_*bph 2 c python swig pointers

我有一个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);
Run Code Online (Sandbox Code Playgroud)

track_t是我无法处理的数据结构的双指针.

所有单指针都可以正常工作.

这是唯一具有双指针的API fn track_t

所有其他只有一个指针,例如

void print_hash_tracks(const track_t *hash_tracks, const cfg_t *cfg,
                       enum TRKTYPE trktype);
Run Code Online (Sandbox Code Playgroud)

我很确定我需要在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;
}
Run Code Online (Sandbox Code Playgroud)

但是当我跑步时,我只是出现了分段错误:

tracks = g3.track_t()
g3.update_tracks(tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
Run Code Online (Sandbox Code Playgroud)

在python方面.

我觉得我几乎已经解决了这个问题,但是无法正确地获得类型图规范,同时也在努力理解相关文档.

柔印 - 如果你在那里 - 也许你可以对此有所了解,你似乎是这方面的SO专家..

更新 - m7ython(太棒了!SO的另一位SWIG专家)

C中的用法非常简单

声明并初始化一个指向NULL的track_t指针:

track_t *hash_tracks = NULL;
Run Code Online (Sandbox Code Playgroud)

然后:

update_tracks(&hash_tracks, &pdw_frame, &rdw_frame,
              &lib, &lib_meta, &cfg);
Run Code Online (Sandbox Code Playgroud)

所以指针的地址track_t作为arg传递给update_tracks().该update_tracks()FN负责所有必要的mallocs的该被装进数据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;
Run Code Online (Sandbox Code Playgroud)

track_t arg是a track_t**而不仅仅是a 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;
}
Run Code Online (Sandbox Code Playgroud)

使用calloc应该确保*phash_tracks为NULL

这个编译和包装没有错误,但是当我从python一侧使用它时,它会被分段,例如

phash_tracks = g3.make_phash_tracks()
g3.update_tracks(phash_tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
Run Code Online (Sandbox Code Playgroud)

phash_tracks在调用之前检查var update_tracks给出了:

(Pdb) p phash_tracks
<Swig Object of type 'track_t **' at 0x7fb9e37c9030>
Run Code Online (Sandbox Code Playgroud)

m7t*_*hon 6

编辑:好的,我想我现在明白了什么update_tracks.看来你可以用两种方式使用这个功能.要么更新现有tracks,要么在tracks指针传递指针时创建NULL.我不确定在SWIG中处理这两种情况的最优雅的方法(或者如果这甚至是一个问题),但这里有一些选择.

1. phash_tracks是输出参数

首先,您必须*phash_tracksPython作为返回值传递回Python,并以某种形式使用该函数

>>> int_res, tracks = g3.update_tracks(tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
Run Code Online (Sandbox Code Playgroud)

要么

>>> int_res, tracks = g3.update_tracks(pdw_frame, rdw_frame, lib, lib_meta, cfg)
Run Code Online (Sandbox Code Playgroud)

这是通过以下"argout"类型映射完成的:

%typemap(argout) track_t **phash_tracks {
  %append_output(SWIG_NewPointerObj(%as_voidptr(*$1), $*1_descriptor, SWIG_POINTER_OWN));
}
Run Code Online (Sandbox Code Playgroud)

也许你不希望Python取得所有权track_t*,然后替换SWIG_POINTER_OWN0.

2.空洞 phash_tracks

如果您只想使用该update_tracks功能进行创建 tracks,您可以基本上完​​成您已经在做的事情.使用以下"in"类型映射,并使用上面第二个示例中的函数(不带tracks参数).

%typemap(in, numinputs=0) track_t **phash_tracks (track_t *tracks) {
  tracks = NULL;
  $1 = &tracks;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,Python禁用它是很重要的tracks_t*.

4.启用上面的(2)和(3)

你基本上也可以使用版本(3)创建 tracks,如果你可以通过swig传递包装NULL tracks_t*.我不确定SWIG是否允许这样做 - 但也许确实如此.尝试使用辅助函数:

tracks_t* empty_tracks() { return NULL; }
Run Code Online (Sandbox Code Playgroud)

或者,您可以沿着以下行修改"in"类型映射,尝试将提供的参数转换为a track_t*并传递其地址,或者传递a的地址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;
}
Run Code Online (Sandbox Code Playgroud)

然后,从Python中,只需传递其他内容即可创建tracks:

>>> int_res, tracks = g3.update_tracks(0, pdw_frame, rdw_frame, lib, lib_meta, cfg)
Run Code Online (Sandbox Code Playgroud)