如何为双指针结构参数应用 SWIG 类型映射
How to apply a SWIG typemap for a double pointer struct argument
我有一个 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
数据结构的双指针。
所有单指针工作正常。
这是唯一的 API fn 具有指向 track_t
的双指针
其他的只有一个指针,例如
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) {
= &tracks;
}
但是当我 运行:
时我遇到了分段错误
tracks = g3.track_t()
g3.update_tracks(tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
python 一侧。
我觉得我几乎已经解决了这个问题,但不能完全正确地理解类型映射规范,同时也在努力理解相关文档。
flexo - 如果你在那里 - 也许你可以对此有所了解,你似乎是这个领域的 SO 专家..
更新 - m7ython(太棒了!另一位 SWIG 专家)
C 中的用法非常简单
声明并初始化一个指向 NULL 的 track_t 指针:
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
的数据处理所有必要的 malloc,即 track_t
结构
的散列 table
所有其他参数都是单指针,我可以在 python 端毫无问题地创建和填充它们。
track_t
是一个包含一堆整数、浮点数、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
是指向散列 table 的指针(使用 UTHash 库)。 hash_tracks
指向散列 table 中的第一个 track_t。在 update_tracks()
fn track_t 结构的主体中,可以从散列 table 中 added/removed,这样指向第一个 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
<Swig Object of type 'track_t **' at 0x7fb9e37c9030>
编辑:好的,我想我现在明白了 update_tracks
的作用。看来您可以通过两种方式使用该功能。要么更新现有 tracks
,要么创建 tracks
(如果将指针传递给 NULL
指针)。我不确定在 SWIG 中处理这两种情况的最优雅方式(或者这是否是一个问题),但这里有一些选择。
1。 phash_tracks
是一个输出参数
首先,您必须将*phash_tracks
作为return值传递回Python,并以某种形式使用该函数,例如
>>> 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_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;
= &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;
= &tracks;
}
请注意,重要的是 Python 否认其 tracks_t*
。
4。同时启用上面的 (2) 和 (3)
你基本上也可以使用版本 (3) 来 创建 tracks
,如果你可以痛饮传递一个包装的 NULL
tracks_t*
.我不确定 SWIG 是否允许这样做——但也许允许。尝试使用辅助函数:
tracks_t* empty_tracks() { return NULL; }
或者,您可以沿以下行修改 "in" 类型映射,尝试将提供的参数转换为 track_t*
并传递其地址,或者传递 [=21] 的地址=] 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;
= &tracks;
}
然后,从 Python 开始,只需传递其他内容即可创建 tracks
:
>>> int_res, tracks = g3.update_tracks(0, pdw_frame, rdw_frame, lib, lib_meta, cfg)
我有一个 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
数据结构的双指针。
所有单指针工作正常。
这是唯一的 API fn 具有指向 track_t
其他的只有一个指针,例如
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) {
= &tracks;
}
但是当我 运行:
时我遇到了分段错误tracks = g3.track_t()
g3.update_tracks(tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
python 一侧。
我觉得我几乎已经解决了这个问题,但不能完全正确地理解类型映射规范,同时也在努力理解相关文档。
flexo - 如果你在那里 - 也许你可以对此有所了解,你似乎是这个领域的 SO 专家..
更新 - m7ython(太棒了!另一位 SWIG 专家)
C 中的用法非常简单
声明并初始化一个指向 NULL 的 track_t 指针:
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
的数据处理所有必要的 malloc,即 track_t
结构
所有其他参数都是单指针,我可以在 python 端毫无问题地创建和填充它们。
track_t
是一个包含一堆整数、浮点数、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
是指向散列 table 的指针(使用 UTHash 库)。 hash_tracks
指向散列 table 中的第一个 track_t。在 update_tracks()
fn track_t 结构的主体中,可以从散列 table 中 added/removed,这样指向第一个 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
<Swig Object of type 'track_t **' at 0x7fb9e37c9030>
编辑:好的,我想我现在明白了 update_tracks
的作用。看来您可以通过两种方式使用该功能。要么更新现有 tracks
,要么创建 tracks
(如果将指针传递给 NULL
指针)。我不确定在 SWIG 中处理这两种情况的最优雅方式(或者这是否是一个问题),但这里有一些选择。
1。 phash_tracks
是一个输出参数
首先,您必须将*phash_tracks
作为return值传递回Python,并以某种形式使用该函数,例如
>>> 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_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;
= &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;
= &tracks;
}
请注意,重要的是 Python 否认其 tracks_t*
。
4。同时启用上面的 (2) 和 (3)
你基本上也可以使用版本 (3) 来 创建 tracks
,如果你可以痛饮传递一个包装的 NULL
tracks_t*
.我不确定 SWIG 是否允许这样做——但也许允许。尝试使用辅助函数:
tracks_t* empty_tracks() { return NULL; }
或者,您可以沿以下行修改 "in" 类型映射,尝试将提供的参数转换为 track_t*
并传递其地址,或者传递 [=21] 的地址=] 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;
= &tracks;
}
然后,从 Python 开始,只需传递其他内容即可创建 tracks
:
>>> int_res, tracks = g3.update_tracks(0, pdw_frame, rdw_frame, lib, lib_meta, cfg)