• Subject: How do I get two dimensional array access notation for my (matrix) userdata?
• From: "E. Wing" <ewmailing@...>
• Date: Sun, 27 Jul 2008 00:10:44 -0700

```I have my own Matrix struct defined in C. I am trying to expose it to
Lua using userdata and metatables.

I'm pretty much following the example from Programming in Lua Chapter 28:
http://www.lua.org/pil/28.html

For simplicity, I have reduced my matrix down to a 2x2 matrix defined in C as:

typedef struct MyMatrix
{
double m11, m12;
double m21, m22;
} MyMatix;

Currently, I can create my userdata in Lua and I have written the
__index and __newindex metamethods so I can access the elements as in
the following in Lua:

local my_matrix = matrix.MyMatrixFromLua(1, 2, 3, 4)
-- access by key name
my_matrix.m11 = 11
my_matrix.m12 = 12
my_matrix.m21 = 21
my_matrix.m22 = 22

-- or by array index
my_matrix[1] = 11
my_matrix[2] = 12
my_matrix[3] = 21
my_matrix[4] = 22

But what I would also really like is to be able to access the data
using a two dimensional array notation, e.g.

my_matrix[2][1] = 21

Is this possible to do? Can somebody explain how I would modify my C
code (which is based on PiL) to do this?

And can this peacefully coexist with what I already have, in
particular, the array index style? So can I have
my_matrix[1][2]
and
my_matrix[2]
both work?

My simple experimentation code is pasted below.

Thanks,
Eric

#include <lua/lua.h>
#include <lua/lualib.h>
#include <lua/lauxlib.h>
#include <string.h>
#include "lua_matrix.h"

typedef struct MyMatrix
{
double m11, m12;
double m21, m22;
} MyMatrix;

const char* MYMATRIX_METATABLEID = "MyMatrix";

static MyMatrix* LuaCheckMyMatrix(lua_State* lua_state, int ud)
{
return luaL_checkudata(lua_state, ud, MYMATRIX_METATABLEID);
}

static int MyMatrixToLua(lua_State* lua_state)
{
MyMatrix* the_struct = LuaCheckMyMatrix(lua_state, -1);
lua_newtable(lua_state);

lua_pushnumber(lua_state, the_struct->m11);
lua_setfield(lua_state, -2, "m11");

lua_pushnumber(lua_state, the_struct->m12);
lua_setfield(lua_state, -2, "m12");

lua_pushnumber(lua_state, the_struct->m21);
lua_setfield(lua_state, -2, "m21");

lua_pushnumber(lua_state, the_struct->m22);
lua_setfield(lua_state, -2, "m22");

return 1;
}

static int MyMatrixFromLua(lua_State* lua_state)
{
int number_of_args = lua_gettop(lua_state);
MyMatrix the_struct;
if(0 == number_of_args)
{
// Allow empty to build the identity matrix
the_struct.m11 = 1.0; the_struct.m12 = 0.0;
the_struct.m21 = 0.0; the_struct.m22 = 1.0;
}
else if(1 == number_of_args)
{
if (!lua_istable(lua_state, -1))
{
luaL_error(lua_state, "Invalid parameters to create MyMatrix");
}

lua_getfield(lua_state, -1, "m11");
the_struct.m11 = luaL_checknumber(lua_state, -1);
lua_pop(lua_state, 1);

lua_getfield(lua_state, -1, "m12");
the_struct.m12 = luaL_checknumber(lua_state, -1);
lua_pop(lua_state, 1);

lua_getfield(lua_state, -1, "m21");
the_struct.m21 = luaL_checknumber(lua_state, -1);
lua_pop(lua_state, 1);

lua_getfield(lua_state, -1, "m22");
the_struct.m22 = luaL_checknumber(lua_state, -1);
lua_pop(lua_state, 1);
}
else if(4 == number_of_args)
{
the_struct.m11 = luaL_checknumber(lua_state, -4);
the_struct.m12 = luaL_checknumber(lua_state, -3);
the_struct.m21 = luaL_checknumber(lua_state, -2);
the_struct.m22 = luaL_checknumber(lua_state, -1);
}
else
{
luaL_error(lua_state, "Invalid parameters to create MyMatrix");
}
void* the_result = lua_newuserdata(lua_state, sizeof(MyMatrix));
memcpy(the_result, &(the_struct), sizeof(MyMatrix));

luaL_getmetatable(lua_state, MYMATRIX_METATABLEID);
lua_setmetatable(lua_state, -2);

return 1;
}

static int ConvertMyMatrixToString(lua_State* lua_state)
{
MyMatrix* the_struct = LuaCheckMyMatrix(lua_state, -1);
lua_pushfstring(lua_state, "\n%f  %f\n%f  %f\n",
the_struct->m11, the_struct->m12,
the_struct->m21, the_struct->m22);
return 1;
}

static int GetIndexOnMyMatrix(lua_State* lua_state)
{
MyMatrix* the_struct = LuaCheckMyMatrix(lua_state, -2);

int array_index = 0;
// allows my_matrix[1] through my_matrix[4]
if(lua_isnumber(lua_state, -1))
{
array_index = lua_tointeger(lua_state, -1);
}
// allows my_matrix.m11 my_matrix.m12, my_matrix.m21, my_matrix.m22
else if(lua_isstring(lua_state, -1))
{
const char* index_string = lua_tostring(lua_state, -1);

if(!strcmp("m11", index_string))
{
array_index = 1;
}
else if(!strcmp("m12", index_string))
{
array_index = 2;
}
else if(!strcmp("m21", index_string))
{
array_index = 3;
}
else if(!strcmp("m22", index_string))
{
array_index = 4;
}
else
{
luaL_error(lua_state, "invalid member access '%s' of MyMatrix
__index", index_string);
}
}
else
{
luaL_error(lua_state, "invalid member access of MyMatrix __index");
}
luaL_argcheck(lua_state, 1 <= array_index && array_index <= 4, -1,
"index out of range");
if(array_index == 1)
{
lua_pushnumber(lua_state, the_struct->m11);
}
else if(array_index == 2)
{
lua_pushnumber(lua_state, the_struct->m12);
}
else if(array_index == 3)
{
lua_pushnumber(lua_state, the_struct->m21);
}
else if(array_index == 4)
{
lua_pushnumber(lua_state, the_struct->m22);
}
return 1;
}

static int SetIndexOnMyMatrix(lua_State* lua_state)
{
MyMatrix* the_struct = LuaCheckMyMatrix(lua_state, -3);
int element_index = 0;

if(lua_isnumber(lua_state, -2))
{
element_index = lua_tointeger(lua_state, -2);
}
else if(lua_isstring(lua_state, -2))
{
const char* index_string = lua_tostring(lua_state, -2);

if(!strcmp("m11", index_string))
{
element_index = 1;
}
else if(!strcmp("m12", index_string))
{
element_index = 2;
}
else if(!strcmp("m21", index_string))
{
element_index = 3;
}
else if(!strcmp("m22", index_string))
{
element_index = 4;
}
else
{
luaL_error(lua_state, "invalid member access '%s' of MyMatrix
__newindex", index_string);
}
}

luaL_argcheck(lua_state, 1 <= element_index && element_index <= 4,
-2, "index out of range");
int new_value = luaL_checknumber(lua_state, -1);

if(element_index == 1)
{
the_struct->m11 = new_value;
}
else if(element_index == 2)
{
the_struct->m12 = new_value;
}
else if(element_index == 3)
{
the_struct->m21 = new_value;
}
else if(element_index == 4)
{
the_struct->m22 = new_value;
}
return 0;
}

static const struct luaL_reg methods_for_mymatrix[] =
{
{"__tostring", ConvertMyMatrixToString},
{"__index", GetIndexOnMyMatrix},
{"__newindex", SetIndexOnMyMatrix},
{NULL,NULL},
};

const luaL_reg lua_mymatrix_functions[] =
{
{"MyMatrixFromLua", MyMatrixFromLua},
{"MyMatrixToLua", MyMatrixToLua},
{NULL,NULL},
};

int luaopen_matrix(lua_State* lua_state)
{
luaL_newmetatable(lua_state, MYMATRIX_METATABLEID);
lua_pushvalue(lua_state, -1);
lua_setfield(lua_state, -2, "__index");
luaL_register(lua_state, NULL, methods_for_mymatrix);

luaL_register(lua_state, "matrix", lua_mymatrix_functions);
return 1;
}

```

• Follow-Ups: