【问题标题】:Lua C API: Initializing a variable matrix in a structure CLua C API:初始化结构C中的变量矩阵
【发布时间】:2017-10-28 13:16:12
【问题描述】:

我正在尝试使用 Lua C API 创建一个用户数据,其中包含一个关联的元表,我将在其中收集一个矩阵。

我无法得到的是如何将初始化矩阵的每个分量设置为零。

我按照here 的描述编译我的 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;
}

相关的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))

我得到的输出是:

userdata: 007C2940
--------------
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
1.#QNAN
--------------
row = 3
col = 3

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

我做错了什么?

【问题讨论】:

    标签: c matrix multidimensional-array lua


    【解决方案1】:

    嗯,你怎么知道的?您可以启动调试器并查看内存中的数据。或者……这是 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;
    }
    

    将该函数作为{"peek",lb_peek} 添加到LuaBasic_m。重新编译,启动 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…)
    

    这看起来不对……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
    

    啊哈……你的矩阵尺寸放错了地方! (看到数据区中间的3、3了吗?)

    你告诉编译器有一个LUA_NUMBERs 的 1x1 数组……但随后继续扩大它。编译器发出的代码假定,给定matrix *mm-&gt;row 位于*(m+sizeof(LUA_NUMBER[1][1])),但这就是您编写数据的地方……

    因此您需要更改struct 字段的顺序:将可变大小的部分放在最后!

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

    修改、重新编译&重启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
    

    这是一个适当的全零 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;
    }
    

    并将{"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
    

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

    再次,您告诉编译器您有一个 1x1 数组。 (编译器不关心你是否在做越界访问,事实上你可以写i[m-&gt;data][j]而不是m-&gt;data[i][j],你会得到完全相同的行为。)所以你不能让编译器做为您计算偏移量,您必须手动完成。声明一个二维数组会妨碍您,所以再次将您的 struct 更改为

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

    ([0] 只是尾随可变大小部分的约定。你可以说data[1],但作为独立的struct 可能是有意义的。data[0] 什么都不是,这作为固定大小的struct 没有意义——所以它相对清楚地传达了(如果你知道这个约定)这应该是可变大小的。)

    然后将所有出现的m-&gt;data[i][j] 更改为m-&gt;data[m-&gt;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
    

    所以代码现在可以工作了。

    您的测试作业的最后一个问题是您访问的是[1][2][3],而不是[0][1][2]C 完全是关于偏移量并使用从零开始的索引。第一个元素位于 [0] 而不是 [1],最后一个元素位于 [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, index 从 1 到 n)或基于 C/offset 的约定(基于 0,从 0 到 (n-1) 的偏移)。 (如果将其用于矩阵数学,最好选择基于 0 的索引/偏移,因为某些算法和公式可能会假设这一点,而您可能不想全部更改。)

    【讨论】:

    • 谢谢,很好的解释。顺便说一句,您的 lb_peek 函数返回一个空字符串。
    • @Azoun lb_peek 应该返回一个主要由零字节组成的字符串(除非您将数据写入矩阵),因此如果您 print 该字符串您将看不到任何内容。 (但是打印长度(#str),查看string.bytes 或string.unpacking 它应该会告诉你有什么东西。)lb_set 以什么方式不起作用? (“它不起作用”没有包含足够的信息来找出它在您和我的系统上的行为不同的原因。)
    • 我觉得原因是我用的是Lua 5.2.4
    • @Azoun 这应该可以解释 一些 差异 - 你没有 string.unpack 所以解释数据会更加困难,但是说例如string.byte( m:peek( size ), 1, size ) 应该向你抛出很多数字,其中大部分是零。 (你会在靠近开始的地方发现两个 '3'。)浮动将很难手动解释......这不会很有趣。 lb_set 应该仍然有效(至少我看不出它不应该的原因),但如果没有 unpack,您可能无法查看/解释结果。
    • lb_set 的编译错误是error: subscripted value is neither array nor pointer nor vector m-&gt;data[i][j] = v;。我从m-&gt;data[i][j] = v; 更改为m-&gt;data[i][j] = v;。现在它起作用了。非常感谢
    【解决方案2】:

    该问题与 Lua 没有直接关系;这主要是一个C问题。简而言之,当temp-&gt;data被动态分配时,你不能写temp-&gt;data[i][j],并希望它能工作,因为C编译器知道temp-&gt;data是一个1x1矩阵。

    要解决这个问题,首先定义

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

    data 是最后一个字段,这一点至关重要。

    然后将矩阵中的位置 i,j线性设为data[i*col + j]

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-30
    • 2012-08-18
    • 1970-01-01
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    • 2019-04-10
    相关资源
    最近更新 更多