如何在 swig 中公开 C 结构内的可变大小数组?

发布于 2025-01-17 09:59:49 字数 3554 浏览 3 评论 0原文

我现在正在努力几天,以找到一种解决方案来包装C结构,其中包含SWIG中的多个可变大小的INT阵列(存储为指针)。 假设以下最小示例:

typedef struct {
  size_t length;
  int    *a;
  int    *b;
} mystruct;

其中abintc中分配的数组的指示。两个数组的大小都存储在长度成员中。 现在,我真正想拥有的是两倍:

  1. 访问ab在类型myStruct的对象中的成员应该是安全的,如果索引不合时宜,则应抛出IE例外。
  2. AB中的数据不得复制到Python列表或元组中,但我想提供__ getItem __方法。这样做的原因是,实际结构由许多这样的阵列组成,它们变得非常巨大,我不想通过复制它们来浪费任何记忆。

我已经看到了如何通过为每个成员编写包装器类和模板,该示例如何通过固定尺寸的阵列来完成此示例,这些成员在内部存储每个数组的大小/长度,例如: swig Interfacing c Intefacing C crublite c python to python(从C'sequence'struct'struct'struct'struct'struct'struct'struct'struct'struct'结构) and swig/python阵列内部结构。 但是,我假设一旦我将ab纳入一类,以使它们能够使用__ getitem __ __方法进行扩展,我将访问长度 myStruct的成员,即ab的“容器”。

我在没有成功的情况下尝试的一件事是编写显式_get_SET方法,

typedef struct {
  size_t length;
} mystruct;

%extend mystruct {
 int *a;
};

%{
  int *mystruct_a_get(mystruct *s) {
    return mx->a;
  }
  int *mystruct_b_get(mystruct *s) {
    return mx->b;
  }
  ...
%}

但是在这里,整个数组 a a and b将在没有最大索引的任何控制的情况下返回...

我的目标语言是Python和Perl 5,所以我想人们可以开始为每种语言编写复杂的打字。我之前已经为其他包装纸做了这件事,并希望我的情况有一个更通用的解决方案,只涉及C ++包装器类别等。

任何帮助或想法都将受到赞赏!

编辑可能的解决方案

因此,我无法放开它,并提出以下(简化)解决方案,这些解决方案或多或少地结合了我在其他地方已经看到的解决方案。想法是为每个包装数组的数组冗余地存储数组长度:

%{
/* wrapper for variable sized arrays */
typedef struct {
  size_t length;
  int    *data;
} var_array_int;

/* convenience constructor for variable sized array wrapper */
var_array_int *
var_array_int_new(size_t length,
                  int    *data)
{
  var_array_int *a = (var_array_int *)malloc(sizeof(var_array_int));
  a->length        = length;
  a->data          = data;

  return a;
}

/* actual structure I want to wrap */
typedef struct {
  size_t length;
  int    *a;
  int    *b;
} mystruct;
%}

/* hide all struct members in scripting language */
typedef struct {} var_array_int;
typedef struct {} mystruct;

/* extend variable sized arrays with __len__ and __getitem__ */
%extend var_array_int {
  size_t __len__() const {
    return $self->length;
  }

  const int __getitem__(int i) const throw(std::out_of_range) {
    if ((i < 0) ||
        (i >= $self->length))
      throw std::out_of_range("Index out of bounds");

    return $self->data[i];
  }
};

/* add read-only variable sized array members to container struct */
%extend mystruct {
  var_array_int *const a;
  var_array_int *const b;
};

/* implement explict _get() methods for the variable sized array members */
%{
  var_array_int *
  mystruct_a_get(mystruct *s)
  {
    return var_array_int_new(s->length, s->a);
  }

  var_array_int *
  mystruct_b_get(mystruct *s)
  {
    return var_array_int_new(s->length, s->b);
  }
%}

以上解决方案仅提供对变量大小数组的读取访问,并且不包括任何null null 检查包装int *指针。我的实际解决方案当然可以做到这一点,还利用模板来包装不同类型的可变大小数组。但是我避免出于清晰的目的表明这里。

我想知道是否有一种更简单的方法可以做上述方法。此外,到目前为止,该解决方案似乎仅在Python中起作用。为Perl 5实施类似的东西已经让我头疼。

I'm struggling for a few days now to find a solution to wrap a C struct containing multiple variable-sized int arrays (stored as pointers) in swig.
Suppose the following minimal example:

typedef struct {
  size_t length;
  int    *a;
  int    *b;
} mystruct;

where both a and b are pointers to int arrays allocated somewhere in C. The size of both arrays is stored in the length member.
Now, what I would really like to have is two-fold:

  1. access to a and b members in objects of type mystruct should be safe, i.e. exceptions should be thrown if index is out-of-bounds.
  2. the data in a and b must not be copied-over into a python list or tuple but I want to provide __getitem__ methods instead. The reason for that is that the actual struct consists of many such arrays and they get really huge and I don't want to waste any memory by duplicating them.

I've seen examples how to accomplish this with fixed-sized arrays by writing wrapper classes and templates for each member that internally store the size/length of each array individually, e.g.: SWIG interfacing C library to Python (Creating 'iterable' Python data type from C 'sequence' struct) and SWIG/python array inside structure.
However, I assume once I would wrap a and b into a class to enable them to be extended with __getitem__ methods, I won't have access to the length member of mystruct, i.e. the 'container' of a and b.

One thing I tried without success was to write explicit _get and _set methods

typedef struct {
  size_t length;
} mystruct;

%extend mystruct {
 int *a;
};

%{
  int *mystruct_a_get(mystruct *s) {
    return mx->a;
  }
  int *mystruct_b_get(mystruct *s) {
    return mx->b;
  }
  ...
%}

But here, the entire arrays a and b would be returned without any control of the maximum index...

My target languages are Python and Perl 5, so I guess one could start writing complicated typemaps for each language. I've done that before for other wrappers and hope there is a more generic solution to my situation that involves only C++ wrapper classes and such.

Any help or idea is appreciated!

Edit for possible solution

So, I couldn't let it go and came up with the following (simplified) solution that more or less combines the solutions I already saw elsewhere. The idea was to redundantly store the array lengths for each of the wrapped arrays:

%{
/* wrapper for variable sized arrays */
typedef struct {
  size_t length;
  int    *data;
} var_array_int;

/* convenience constructor for variable sized array wrapper */
var_array_int *
var_array_int_new(size_t length,
                  int    *data)
{
  var_array_int *a = (var_array_int *)malloc(sizeof(var_array_int));
  a->length        = length;
  a->data          = data;

  return a;
}

/* actual structure I want to wrap */
typedef struct {
  size_t length;
  int    *a;
  int    *b;
} mystruct;
%}

/* hide all struct members in scripting language */
typedef struct {} var_array_int;
typedef struct {} mystruct;

/* extend variable sized arrays with __len__ and __getitem__ */
%extend var_array_int {
  size_t __len__() const {
    return $self->length;
  }

  const int __getitem__(int i) const throw(std::out_of_range) {
    if ((i < 0) ||
        (i >= $self->length))
      throw std::out_of_range("Index out of bounds");

    return $self->data[i];
  }
};

/* add read-only variable sized array members to container struct */
%extend mystruct {
  var_array_int *const a;
  var_array_int *const b;
};

/* implement explict _get() methods for the variable sized array members */
%{
  var_array_int *
  mystruct_a_get(mystruct *s)
  {
    return var_array_int_new(s->length, s->a);
  }

  var_array_int *
  mystruct_b_get(mystruct *s)
  {
    return var_array_int_new(s->length, s->b);
  }
%}

The above solution only provides read access to the variable sized arrays and does not include any NULL checks for the wrapped int * pointers. My actual solution of course does that and also makes use of templates to wrap variable sized arrays of different types. But I refrained from showing that here for the sake of clarity.

I wonder if there is an easier way to do the above. Also the solution only seems to work in Python so far. Implementing something similar for Perl 5 already gives me a headache.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文