lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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;
}