创建 SWIG 的类型映射,使得 C 函数可以返回 Lua 表

我正在尝试包装一个创建浮点数组并将其作为 Lua 表返回以便在 Lua 中使用的 C 函数。

以下是返回具有 4 个元素的浮点数组的 C 函数。

static void getArray(int size, float values[4]) {

    for (int i=0; i<size; ++i)
        values[i] = (float)i;
}

这是在 .i 文件中的类型映射部分。

// 使用类型映射
%include <typemaps.i>
%apply (float OUTPUT[ANY]) {(float values[4])};

在 Lua 中,我可以使用以下方式使用该函数:

arr = my.getArray(4); //表"arr"现在为{0,1,2,3}

虽然这很好用,但我想知道是否可能创建一个可以返回可变浮点数组的 C 函数。

因此,我认为该函数将类似于以下内容。

static void getArray(int size, float **values) {

    //创建一个浮点数组,然后将其返回为 Lua 中的表。
}

但是,我不知道如何使用 SWIG 接口 (.i) 绑定此函数。

我尽我所能尝试了一切,但到目前为止都没能成功。

请问有谁可以指导我如何使用 SWIG 包装此函数以便我可以返回 Lua 中的可变浮点数组作为表?

P.S:这是 Lua SWIG 文档的链接。 http://www.swig.org/Doc1.3/Lua.html

------------------------以下是补充内容------------------------

@Flexo 基于您的更新解决方案,我成功地绑定了以下 getArray 函数。

static void getArray(const string &name, int *size, t_word **values) {

        t_garray *a;
        int vecsize;
        t_word *vec;

        if (getArrayData(name, &a, &vecsize, &vec)) {

            *values = vec;
            *size = vecsize;
        }
    }

这是我的 SWIG 接口。

%typemap(in,numinputs=0) (int *size, t_word **values) (t_word *tmp=NULL, int tsize=0) %{
  $2 = &tmp; // 使用我们设置的临时变量
  $1 = &tsize;
%}

%typemap(argout) (int *size, t_word **values) {

  int i;
  lua_newtable(L);
  for (i = 0; i < *$1; i++){
    lua_pushnumber(L,(lua_Number)(*$2)[i].w_float);
    lua_rawseti(L,-2,i+1);/* -1 代表数字,-2 代表表*/
  }
  SWIG_arg++;
}

t_word 是用于包含浮点数据 w_float 的数组的结构体。

由于我没有分配新的内存,因此我不需要释放任何内容,它似乎运行得非常好。

非常感谢您的帮助。我从您的代码中学到了很多。

点赞
用户168175
用户168175

使用多参数类型映射可以完成此操作。我基于typemaps.i中的OUTPUT [ANY]的类型映射,编写了一个快速测试以展示如何工作:


%typemap(in,numinputs=1) (int size, float **values) (float *tmp=NULL) %{
    $2 =&tmp; //使用我们设置的临时变量
    //以下两行等同
    //$1 =(int)lua_tonumber(L,$input);
    $typemap(in,int);
%}

%typemap(freearg)(int size, float **values) %{
    free(tmp$argnum); //假定这里是正确的语义
%}

%typemap(argout)(int size, float **values) {
    //从typemaps.i中的OUTPUT [ANY] argout复制而来
    int i;
    lua_newtable(L);
    for (i = 0; i < $1; i++){
        lua_pushnumber(L,(lua_Number)(*$2)[i]);
        lua_rawseti(L,-2,i+1);// -1是数字,-2是表* /
    }
    SWIG_arg++;
} 

%inline %{
static void getArray(int size, float **values) {
    //创建一个浮点数组,然后将其作为Lua中的表返回。
    float *arr = malloc(sizeof *arr * size);
    for (int i = 0; i < size; ++i) {
        arr[i] = i;
    }
    *values = arr;
}

从Lua中,我们可以像这样使用这个函数:

Lua 5.3.3  Copyright (C) 1994-2016 Lua.org, PUC-Rio
> l=require('test')
> l
table: 0xcb87de8
> l.getArray()
stdin:1: Error in getArray expected 1..1 args, got 0
stack traceback:
        [C]: in function 'test.getArray'
        stdin:1: in main chunk
        [C]: in ?
> l.getArray(1)
table: 0xcb86d48
> l.getArray(1)
table: 0xcb88b50
> l.getArray(1)[0]
nil
> l.getArray(1)[1]
0.0
> l.getArray(1)[2]
nil
> l.getArray(3)[2]
1.0
> l.getArray(3)[3]
2.0
> l.getArray(3)[4]
nil

在这里大部分工作都是在argout类型映射中完成的,它创建一个新表并用我们的getArray()函数放入float **输出参数中的值进行填充。

如果实际的getArray()版本实际上没有让您指定或找出输出的长度,那么最好根本不对其进行包装,而是专注于包装getArrayData()本身。(如果需要,您可以使用%rename让接口感觉对于已经熟悉C ++ API的用户更为相似)。对于包装getArrayData(),我会更改它,例如:

%module test
%include <std_string.i>
%typemap(in,numinputs=0) (int *size, float **values) (float *tmp=NULL,int tsize=0) %{
    $2 =&tmp;
    $1 =&tsize;
%}

%typemap(argout)(int *size, float **values) {
    //从typemaps.i中的OUTPUT [ANY] argout复制而来
    int i;
    lua_newtable(L);
    for (i = 0; i < *$1; i++){
        lua_pushnumber(L,(lua_Number)(*$2)[i]);
        lua_rawseti(L,-2,i+1);// -1是数字,-2是表* /
    }
    SWIG_arg++;
} 

%inline %{
static void getArrayData(const std::string& name, void *something_else, int *size, float **values) {
    //假装有人已经拥有了这个内存
    static float arr[100];
    *size = sizeof arr / sizeof *arr;
    for (int i = 0; i <*size; ++i) {
        arr[i] = i;
    }
    *values = arr;
}
%}

关键更改:

  • 不需要freearg typemap,因为我们实际上不需要分配任何内存
  • numinputs=0,因为int *size和float **values都是输出

-类型映射内的额外本地变量,用于size输出

2018-06-04 07:29:01