Lua C API:在结构C中初始化变量矩阵 [英] Lua C API: Initializing a variable matrix in a structure C

查看:79
本文介绍了Lua C API:在结构C中初始化变量矩阵的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to create an userdata with Lua C API with a metatable associated where I will collect a matrix.

我不知道如何将初始化矩阵的每个分量设置为零.

What I can't get is how to set every component of the initialized matrix to zero.

我按照我的描述我的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代码如下:

The related Lua Code is the following:

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

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

I don't understand why I can't write the zero values into initialized matrix.

我该怎么办?

推荐答案

好吧,您怎么发现呢?您可以启动调试器并查看内存中的数据.或者...这是Lua,您可以扩展您的库以使其变得容易.因此,如果您想继续……(否则,请跳过引号框)

Well, how could you find out? You could start a debugger and look at the data in memory. Or… this being Lua, you could extend your library to make that easy. So, if you want to follow along… (otherwise just skip the quote blocks)

lb_newmatrix的末尾(恰好在return之前),添加lua_pushinteger( L, nbyte );并将return 1;更改为return 2;. (这只是方便,因此我们不必重新计算大小.)此外,添加

At the end of lb_newmatrix (just before the return), add lua_pushinteger( L, nbyte ); and change the return 1; to return 2;. (This is just convenience so we don't have to re-compute the size.) Further, add

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解释器并加载库.

and add that function to LuaBasic_m as {"peek",lb_peek}. Recompile, fire up the Lua interpreter and load the library.

> 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不应该在那里.让我们看看它是什么样的整数……

This doesn't look right… That 6.366e-314 shouldn't be there. Let's look at what this looks like as integers…

> 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

A-ha ...您的矩阵尺寸在错误的位置! (请参见数据区域中间的3、3吗?)

A-ha… your matrix dimensions are in the wrong place! (See the 3, 3 in the middle of the data area?)

您要告诉编译器,其中有一个LUA_NUMBER s的1x1数组…,然后继续增大它的大小.编译器发出的代码假定给定matrix *mm->row位于*(m+sizeof(LUA_NUMBER[1][1])),但这是您编写数据的地方……

You're telling the compiler that there's a 1x1 array of LUA_NUMBERs… but then proceed to make it larger. The compiler emits code that assumes that, given a matrix *m, m->row is at *(m+sizeof(LUA_NUMBER[1][1])) but that's where you write your data…

因此,您需要更改struct字段的顺序:最后保留可变大小的零件!

So you need to change the order of your struct's fields: Keep variable-sized parts last!

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

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

After changing, recompiling & restarting Lua, we can check:

> 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矩阵.现在,让我们检查数据放置.因为我们不想一直重新编译以测试新的分配,所以添加

which is a proper all-zero 3x3 matrix. Now let's check the data placement. Because we don't want to re-compile all the time to test new assignments, add

/* 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.重新编译,重新加载:

and add {"set",lb_set} to LuaBasic_m. Recompile, reload:

> 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)(或任何矩阵大小) ).

hmm… see any pattern? :-) The data is written at data+((1*i)+j)*sizeof(lua_Number), not data+((3*i)+j)*sizeof(lua_Number) (or whatever the size of your matrix).

同样,您要告诉编译器您有一个1x1数组. (编译器并不关心您是否在进行越界访问,实际上您可以编写i[m->data][j]而不是m->data[i][j],并且会得到完全相同的行为.)因此,您不能让编译器执行以下操作:为您计算偏移量,则必须手动进行.声明二维数组只会妨碍您的操作,因此请再次将struct更改为

Again, you're telling the compiler that you have a 1x1 array. (The compiler doesn't care that you're doing out-of bounds accesses, in fact you could write i[m->data][j] instead of m->data[i][j] and you'd get exactly the same behavior.) So you cannot let the compiler do the offset computations for you, you'll have to do that manually. Declaring a two-dimensional array just gets in the way, so change your struct once more to

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

([0]只是尾随可变大小部分的约定.您可以说data[1],但这对于独立的struct可能是有意义的.data[0]根本不是什么,这作为固定大小的struct没有意义-因此它可以相对清楚地传达(如果您知道此约定)应该是可变大小的.)

(The [0] is just a convention for a trailing variable-sized part. You could say data[1], but that might make sense as a stand-alone struct. data[0] is nothing at all, which doesn't make sense as a fixed-sized struct – so it communicates relatively clearly (if you know about this convention) that this is supposed to be variable-sized.)

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

Then change all occurrences of m->data[i][j] to m->data[m->col*i + j]. (Just try to compile and the compiler will give errors for the lines you need to adjust.)

最终测试:

A final test:

> 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

所以代码现在可以正常工作了.

So the code works now.

测试分配的最后一个问题是您正在访问[1][2][3],而不是[0][1][2]. C都是关于偏移量的,并使用从零开始的索引.第一个元素位于[0]而不是[1],最后一个元素位于[size-1]而不是[size].虽然您可以分配一个(n + 1)x(m + 1)个矩阵,然后可以使用1…n和1…m,但这会浪费空间(3x3不会占用太多空间,但是如果矩阵变大,则会越来越多).因此,最好将代码调整为C约定.

The final problem with your test assignments is that you're accessing [1], [2], [3] instead of [0], [1], [2]. C is all about offsets and uses zero-based indexing. The first element is at [0] not [1] and the last element is at [size-1] instead of [size]. While you could allocate a (n+1)x(m+1) matrix and could then use 1…n and 1…m, that would waste space (not much for 3x3, but more and more if the matrices get bigger). So it's probably better to adjust your code to the C convention.

您还必须修改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的索引/偏移量,因为某些算法和公式可能会以此为前提,并且您可能不想全部更改.)

You will also have to modify your Lua-visible access functions, they currently allow out-of-bounds access. (data[3][3] is outside of a 3x3 matrix, the "outermost" field is data[2][2] or data[m-1][n-1].) You'll have to decide whether you want to stick to the Lua/index-based convention (1-based, index goes from 1 to n) or to the C/offset-based convention (0-based, offset from 0 to (n-1)). (If this is to be used for matrix math, it may be better to pick 0-based indexing/offsetting, as some algorithms and formulas might assume this and you probably don't want to change them all.)

这篇关于Lua C API:在结构C中初始化变量矩阵的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆