Lua C API:在结构C中初始化变量矩阵

Azo*_*oun 4 c lua matrix multidimensional-array

我正在尝试使用Lua C API创建一个userdata,其中有一个metatable关联,我将收集一个矩阵.

我不能得到的是如何将初始化矩阵的每个分量设置为零.

我正如我在此描述的那样编译我的Lua模块C代码.

我的C代码如下:

#include "lauxlib.h"
#include "lua.h"

typedef struct {
    LUA_NUMBER data[1][1];
    int row;
    int col;
}matrix;


// Create a matrix full of zeros
static int lb_newmatrix(lua_State *L)
{

    // Variable declarations
    int i,j;
    matrix *temp;

    // Input checks
    if (lua_gettop(L)!=2)
    {
       lua_pushstring(L,"\n Two input required");
       lua_error(L);
    }

    //--> Check I° index m riga
    luaL_checktype(L,1,LUA_TNUMBER);
    if (lua_tonumber(L,1)<0)
    {
        lua_pushstring(L,"\nRow number must be positive");
        lua_error(L);
    }

    //--> Check II° index n colonna
    luaL_checktype(L,2,LUA_TNUMBER);
    if (lua_tonumber(L,2)<0)
    {
        lua_pushstring(L,"\nColumn number must be positive");
        lua_error(L);
    }

    // Computation of memory allocation
    int m = lua_tonumber(L,1);
    int n = lua_tonumber(L,2);
    size_t nbyte = 2*sizeof(int)+sizeof(LUA_NUMBER)*m*n;
    size_t nbyte2 = sizeof(matrix)+sizeof(LUA_NUMBER)*(m*n-1);

    // Memory allocation
    temp = (matrix *)lua_newuserdata(L,nbyte);

    // Matrix dimension setting
    temp->row = m;
    temp->col = n;

    // Matrix inizialization
    /* PROBLEM HERE */
    for (i=1;i==m;i++)
    {
        for(j=1;j==n;j++)
        {
            temp->data[i][j] = 0;
        }
    }

    //-------------------------------
    // If I de-comment these line,
    // the matrix is written but 
    // element with equal sum indices
    // rewrite!!!
    //-------------------------------
    // temp->data[1][1] = nbyte;
    // temp->data[1][2] = nbyte2;
    // temp->data[1][3] = 13;
    // temp->data[2][1] = nbyte2;
    // temp->data[2][2] = 22;
    // temp->data[2][3] = 23; 
    // temp->data[3][1] = 31;
    // temp->data[3][2] = 32;
    // temp->data[3][3] = 33;

    // Link the userdata to the metatable "basic"
    luaL_getmetatable(L,"basic");
    lua_setmetatable(L,-2);

    return 1;
}

static int lb_index(lua_State *L)
{
    /* Check input Numbers */
    if (lua_gettop(L)>3)
    {
       lua_pushstring(L,"\nOnly two inputs are needed:\n1) Point\n2) N° row\n3) N° col");
       lua_error(L);
    }

    /* Check if the first input is userdata basic */
    matrix *temp = (matrix *)luaL_checkudata(L,1,"basic");

    /* I° index check ROW */
    luaL_checktype(L,2,LUA_TNUMBER); 
    if (lua_tointeger(L,2)<0||lua_tointeger(L,2)>temp->row)
    {
        lua_pushstring(L,"\n First index should be 1 to n");
        lua_error(L);
    }

    /* II° index check COLUMN */
    luaL_checktype(L,3,LUA_TNUMBER);
    if (lua_tointeger(L,3)<0||lua_tointeger(L,3)>temp->col)
    {
        lua_pushstring(L,"\n Second index should be 1 to m");
        lua_error(L);
    }

    int row = lua_tointeger(L,2);
    int col = lua_tointeger(L,3);

    /* Insert the index value of userdata on top of the stack */
    lua_pushnumber(L,temp->data[row][col]);

    return 1;
}


/**********************
 * MODULE DECLARATION *
 **********************/
static const struct luaL_Reg LuaBasic_f [] = {//
        {"NewMatrix",lb_newmatrix},
        {   "__index",  lb_index},
        {       NULL,        NULL}};

static const struct luaL_Reg LuaBasic_m [] = {//
        {        NULL,      NULL}};

LUA_API int luaopen_LuaBasic(lua_State *L)
{
    /* Insert basic metatable  "basic" into the stack */
    luaL_newmetatable(L,"basic");

    /* Copy the "basic" metatable
       and push it into the stack */
    lua_pushvalue(L,-1);

    /* basic["__index"] = basic */
    lua_setfield(L,-2,"__index");

    /* register all the function
       into LuaBasic_m into the
       basic table metatable */
    luaL_setfuncs(L,LuaBasic_m,0);

    luaL_newlib(L,LuaBasic_f);
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

相关的Lua代码如下:

lb = require("LuaBasic")
A = lb.NewMatrix(3,3)
print(A)
print("--------------")
print(lb.__index(A,1,1))
print(lb.__index(A,1,2))
print(lb.__index(A,1,3))
print(lb.__index(A,2,1))
print(lb.__index(A,2,2))
print(lb.__index(A,2,3))
print(lb.__index(A,3,1))
print(lb.__index(A,3,2))
print(lb.__index(A,3,3))
print("--------------")
print("row = "..lb.GetRow(A))
print("col = "..lb.GetCol(A))
Run Code Online (Sandbox Code Playgroud)

我得到的输出是:

userdata: 007C2940
--------------
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
--------------
row = 3
col = 3
Run Code Online (Sandbox Code Playgroud)

我不明白为什么我不能将零值写入初始化矩阵.

我做错了什么?

nob*_*ody 5

好吧,你怎么知道的?您可以启动调试器并查看内存中的数据.或者......这是Lua,您可以扩展您的库以使其变得简单.所以,如果你想跟随......(否则只是跳过引用块)

lb_newmatrix(就在return)之前,添加lua_pushinteger( L, nbyte );并更改return 1;return 2;.(这只是方便,所以我们不必重新计算尺寸.)此外,添加

static int lb_peek( lua_State *L ) {
    int nbytes = luaL_checkinteger( L, 2 );
    char *data = (char*)luaL_checkudata(L,1,"basic");
    lua_pushlstring( L, data, nbytes );
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

并将该函数添加到LuaBasic_mas {"peek",lb_peek}.重新编译,启动Lua解释器并加载库.

> m, size = lb.NewMatrix( 3, 3 )  -- make a small matrix
> data = m:peek( size )           -- get the memory as a string
> (("n "):rep(3*3).."I I"):unpack( data ) -- and unpack the values
0.0 6.366e-314 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 81
-- (note: the trailing '81' is the offset of the next character in
-- the string beyond what was read and not part of your data)
-- so your matrix looks like:
-- 0.0 6.366e-314 0.0
-- 0.0    0.0     0.0
-- 0.0    0.0     0.0
-- and has 0 rows and 0 columns (according to the data…)
Run Code Online (Sandbox Code Playgroud)

这看起来不对......那6.366e-314不应该存在.让我们来看看整数看起来像什么......

> size/4
20
> (("I4"):rep(20)):unpack( data )
0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 81
Run Code Online (Sandbox Code Playgroud)

A-ha ...你的矩阵尺寸在错误的地方!(参见数据区中间的3,3?)

你告诉编译器有一个1x1的LUA_NUMBERs 数组......然后继续把它做大.编译器发射出假定,给定一个代码matrix *m,m->row*(m+sizeof(LUA_NUMBER[1][1]))但这是你写你的数据...

因此,您需要更改struct字段的顺序: 保持可变大小的部分最后!

 typedef struct {
     int row;
     int col;
     LUA_NUMBER data[1][1];
 } matrix;
Run Code Online (Sandbox Code Playgroud)

更改,重新编译和重新启动Lua后,我们可以检查:

> m, size = lb.NewMatrix( 3, 3 )
> data = m:peek( size )
> ("I I"..("n "):rep(3*3)):unpack( data )
3 3  0.0 0.0 0.0  0.0 0.0 0.0  0.0 0.0 0.0   81
Run Code Online (Sandbox Code Playgroud)

这是一个适当的全零3x3矩阵.现在让我们检查数据放置.因为我们不想一直重新编译以测试新的分配,所以添加

/* m[i][j] = v */
static int lb_set( lua_State *L ) {
    matrix *m = (matrix*)luaL_checkudata(L,1,"basic");
    int i = luaL_checkinteger( L, 2 );
    int j = luaL_checkinteger( L, 3 );
    lua_Number v = luaL_checknumber( L, 4 );
    m->data[i][j] = v;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

并添加{"set",lb_set}LuaBasic_m.重新编译,重新加载:

> m, size = lb.NewMatrix( 3, 3 )
> for i = 0, 2 do for j = 0, 2 do
>>   m:set( i, j, (i+1) + (j+1)/10 )
>>   print( ("I I"..("n "):rep(3*3)):unpack( m:peek( size ) ) )
>> end end
3 3  1.1 0.0 0.0  0.0 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 1.2 0.0  0.0 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 1.2 1.3  0.0 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 2.1 1.3  0.0 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 2.1 2.2  0.0 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 2.1 2.2  2.3 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 2.1 3.1  2.3 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 2.1 3.1  3.2 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 2.1 3.1  3.2 3.3 0.0  0.0 0.0 0.0   81
Run Code Online (Sandbox Code Playgroud)

嗯...看到任何模式?:-)数据写入,而不是(或矩阵的大小).data+((1*i)+j)*sizeof(lua_Number)data+((3*i)+j)*sizeof(lua_Number)

再一次,你告诉编译器你有一个1x1数组.(编译器并不关心你是在进行越界访问,实际上你可以编写i[m->data][j]代替m->data[i][j]而且你会得到完全相同的行为.)所以你不能让编译器为你做偏移计算,你我必须手动完成.声明一个二维数组只会阻碍,所以struct再次改变你的

 typedef struct {
     int row;
     int col;
     LUA_NUMBER data[0];
 } matrix;
Run Code Online (Sandbox Code Playgroud)

(这[0]只是一个尾随变量大小的部分的惯例.你可以说data[1],但这可能是一个独立的有意义struct. data[0]什么都没有,这是固定大小没有意义struct- 所以它相对沟通很明显(如果你知道这个约定)这应该是可变大小的.)

然后将所有出现的更改m->data[i][j]m->data[m->col*i + j].(只是尝试编译,编译器会为你需要调整的行给出错误.)

最后的测试:

> m, size = lb.NewMatrix( 3, 3 )
> for i = 0, 2 do for j = 0, 2 do
>>   m:set( i, j, (i+1) + (j+1)/10 )
>>   print( ("I I"..("n "):rep(3*3)):unpack( m:peek( size ) ) )
>> end end
3 3  1.1 0.0 0.0  0.0 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 1.2 0.0  0.0 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 1.2 1.3  0.0 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 1.2 1.3  2.1 0.0 0.0  0.0 0.0 0.0   81
3 3  1.1 1.2 1.3  2.1 2.2 0.0  0.0 0.0 0.0   81
3 3  1.1 1.2 1.3  2.1 2.2 2.3  0.0 0.0 0.0   81
3 3  1.1 1.2 1.3  2.1 2.2 2.3  3.1 0.0 0.0   81
3 3  1.1 1.2 1.3  2.1 2.2 2.3  3.1 3.2 0.0   81
3 3  1.1 1.2 1.3  2.1 2.2 2.3  3.1 3.2 3.3   81
Run Code Online (Sandbox Code Playgroud)

所以代码现在可以运行了.

与试验任务的最后一个问题是,你所访问[1],[2],[3]而不是[0],[1],[2]. C完全是关于偏移并使用从零开始的索引. 第一个元素是[0]not [1]而最后一个元素是at [size-1]而不是[size].虽然你可以分配一个(n + 1)x(m + 1)矩阵然后可以使用1 ... n和1 ... m,这会浪费空间(3x3不多,但如果矩阵变大则越来越多).因此,将代码调整为C约定可能更好.

您还必须修改Lua可见访问功能,它们目前允许越界访问.(data[3][3]在3x3矩阵之外,"最外层"字段是data[2][2]data[m-1][n-1].)你必须决定是否要坚持基于Lua /索引的约定(从1开始,索引从1到n)或者基于C /偏移量的约定(从0开始,从0到(n-1)的偏移).(如果要将其用于矩阵数学,那么选择基于0的索引/偏移可能会更好,因为某些算法和公式可能会假设这一点而您可能不想全部更改它们.)