我正在尝试使用Lua C API创建一个userdata,其中包含一个metatable,我将收集一个矩阵。
我不能得到的是如何将初始化矩阵的每个分量设置为零。
我按照我所描述的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
我不明白为什么我不能将零值写入初始化矩阵。
我做错了什么?
答案 0 :(得分:5)
在
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; }
并将该函数添加到
LuaBasic_m
{"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…)
这看起来不正确......
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
A-ha ...你的矩阵尺寸在错误的地方! (参见数据区中间的3,3?)
你告诉编译器有一个1x1的LUA_NUMBER
数组......然后继续把它做大。编译器发出的代码假定,matrix *m
,m->row
位于*(m+sizeof(LUA_NUMBER[1][1]))
,但这是您编写数据的地方......
因此,您需要更改struct
字段的顺序:保持可变大小的部分最后!
typedef struct {
int row;
int col;
LUA_NUMBER data[1][1];
} matrix;
更改后,重新编译&amp;重新启动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
{ {1}}(或任何矩阵的大小)。
同样,你告诉编译器你有一个1x1数组。 (编译器并不关心你是在进行越界访问,实际上你可以编写*i)+j)*sizeof(lua_Number)
而不是i[m->data][j]
并且你会得到完全相同的行为。)所以你不能让编译器为你做偏移计算,你必须手动完成。声明二维数组只会妨碍,所以再次将m->data[i][j]
更改为
struct
( typedef struct {
int row;
int col;
LUA_NUMBER data[0];
} matrix;
只是一个尾随变量大小的部分的约定。你可以说[0]
,但这可能是一个独立的data[1]
。{{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
,[1]
而不是[2]
,[3]
,{{1 }}。 C是关于偏移量并使用从零开始的索引。第一个元素位于[0]
而不是[1]
,最后一个元素位于[2]
而不是{{ 1}}。虽然你可以分配一个(n + 1)x(m + 1)矩阵然后可以使用1 ... n和1 ... m,这会浪费空间(3x3不多,但如果矩阵变大则越来越多)。因此,最好将代码调整为C约定。
您还必须修改Lua可见访问功能,它们目前允许越界访问。 ([0]
位于3x3矩阵之外,“最外层”字段为[1]
或[size-1]
。)您必须决定是否要坚持使用Lua / index约定(从1开始,索引从1到n)或基于C /偏移的约定(从0开始,从0到(n-1)的偏移)。 (如果要将其用于矩阵数学,那么选择基于0的索引/偏移可能会更好,因为某些算法和公式可能会假设这一点,您可能不想全部更改它们。)
答案 1 :(得分:4)
问题与Lua没有直接关系;它主要是一个C问题。简而言之,当动态分配temp->data[i][j]
时,您无法编写temp->data
,并希望它能正常工作,因为C编译器知道temp->data
是1x1矩阵。
要解决此问题,请先定义
typedef struct {
int row;
int col;
LUA_NUMBER data[1];
}matrix;
data
是最后一个字段至关重要。
然后将矩阵线性中的位置i,j称为data[i*col + j]
。