Я пытаюсь создать userdata с Lua C API с метатабельной привязкой, где я собираю матрицу.

Чего я не могу понять, так это как обнулить каждый компонент инициализированной матрицы.

Я компилирую свой код модуля Lua на языке C, как я описал -windows " > .

У меня есть код 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

Я не понимаю, почему я не могу записать нулевые значения в инициализированную матрицу.

Что я делаю не так?

4
Azoun 28 Май 2017 в 01:01

2 ответа

Лучший ответ

Ну как ты мог узнать? Вы можете запустить отладчик и посмотреть на данные в памяти. Или ... это 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;
}

и добавьте эту функцию в 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

А-ха ... ваши размеры матрицы находятся не в том месте! (См. 3, 3 в середине области данных?)

Вы говорите компилятору, что есть массив 1x1 LUA_NUMBER s… но затем продолжаете увеличивать его. Компилятор выдает код, который предполагает, что, учитывая matrix *m, m->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 { {X5}} (или любой другой размер вашей матрицы).

Опять же, вы говорите компилятору, что у вас есть массив 1x1. (Компилятору не важно, что вы делаете доступ за пределы допустимого, на самом деле вы можете написать i[m->data][j] вместо m->data[i][j], и вы получите точно такое же поведение.) Поэтому вы не можете позволить компилятор выполняет вычисления смещения за вас, вам придется делать это вручную. Объявление двумерного массива просто мешает, поэтому измените ваш struct еще раз на

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

([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

Так что код работает сейчас.

Последняя проблема с тестовыми заданиями заключается в том, что вы получаете доступ к [1], [2], [3] вместо [0], [1], [2]. C посвящен смещениям и использует индексацию с нуля. Первый элемент находится в [0], а не [1], а последний элемент находится в [size-1] вместо {{ X9 } } . Хотя вы можете выделить (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 / index-based соглашение (на основе 1, индекс идет от 1 до n) или к соглашению на основе C / offset (на основе 0, смещение от 0 до (n-1)). (Если это будет использоваться для математической математики, может быть лучше выбрать индексирование / смещение на основе 0, так как некоторые алгоритмы и формулы могут принять это, и вы, вероятно, не хотите изменять их все.)

5
nobody 28 Май 2017 в 02:41

Эта проблема не имеет прямого отношения к Луа; это в основном проблема С. Короче говоря, вы не можете написать 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].

4
lhf 28 Май 2017 в 01:29