lua-users home
lua-l archive

Re: Conceptual problem with gettable tag method [repost]

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


Falko Poiker wrote:
> One problem I see right away, though with:
> 
> localvec = newVector(10, 10, 10)
> localvec = GlobalVec
> 
> will result in a memory leak, because the vector created by newVector will
> now have nothing pointing to it. I'm assuming the "gc" tagmethod wouldn't
> take care of this situation.
Of course it does! That's the only reason for the existence of the "gc"
method: it tells you when an object is no longer used. newVector mallocs
an object and the "gc" callback frees it.
For what you want: don't touch the set/getglobal methods. They are not
meant to convert data. Just decide how you want to have your vectors -
as userdata or lua-tables. Don't try to mix it, it's just confusing *g*
Attached is an example to show you a possible implementation. It's a
typical textbook example on how to implement a vector type as userdata
and how to overload the operators.
Ciao, ET.
#/*
gcc vector.c -ovector -O -Iinclude -Llib -llualib -llua -lm
exit
#*/
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include <stdio.h>
#include <malloc.h>
static int vectag;
struct vec { double x,y,z; };
static struct vec *check_vec(int argno)
{
 lua_Object o = lua_getparam(argno);
 luaL_arg_check(lua_tag(o) == vectag, argno, "vector expected");
 return lua_getuserdata(o);
}
static struct vec *opt_vec(int argno)
{
 lua_Object o = lua_getparam(argno);
 return lua_tag(o) == vectag ? lua_getuserdata(o) : 0;
}
static double *check_field(struct vec *vec, int argno)
{
 char *field = lua_getstring(lua_getparam(argno));
 if (field && field[0] && !field[1])
	switch (field[0]) {
	 case 'x': return &vec->x;
	 case 'y': return &vec->y;
	 case 'z': return &vec->z;
	}
 luaL_argerror(argno, "illegal vector field");
 return 0; /* shut up compiler */
}
static void newvec(void)
{
 struct vec tmp, *nv;
 struct vec *v = opt_vec(1);
 if (!v) {
	v = &tmp;
	v->x = luaL_opt_number(1, 0);
	v->y = luaL_opt_number(2, 0);
	v->z = luaL_opt_number(3, 0);
 }
 nv = malloc(sizeof(struct vec));
 if (!nv)
	lua_error("out of memory");
 *nv = *v;
 lua_pushusertag(nv, vectag);
}
static void gcvec(void)
{
 free(check_vec(1));
}
static void getvec(void)
{
 lua_pushnumber(*check_field(check_vec(1), 2));
}
static void setvec(void)
{
 *check_field(check_vec(1), 2) = luaL_check_number(3);
}
static void vecopen(void)
{
 vectag = lua_newtag();
 lua_pushcfunction(getvec);
 lua_settagmethod(vectag, "gettable");
 lua_pushcfunction(setvec);
 lua_settagmethod(vectag, "settable");
 lua_pushcfunction(gcvec);
 lua_settagmethod(vectag, "gc");
 lua_register("vec", newvec);
}
int main(int argc, char **argv)
{
 if (argc != 2) {
	fprintf(stderr, "usage: %s <file>\n", argv[0]);
	exit(1);
 }
 lua_open();
 lua_userinit();
 vecopen();
 if (lua_dofile(argv[1])) {
	fprintf(stderr, "%s: error executing file '%s'\n", argv[0], argv[1]);
	exit(1);
 }
 exit(0);
 return 0;
}
#!./vector
tag_vec = tag(vec())
tag_nil = tag(nil)
tag_num = tag(0)
-- first some helper functions (to add a "tostring" tag method)
-- a new settagmethod with hook for new methods
settagmethod_hook = {}
function settagmethod(tag, method, tm)
 local fn = settagmethod_hook[method] or %settagmethod
 return fn(tag, method, tm)
end
-- a new tostring with hook for new types
tostring_hook = {}
function tostring(o)
 local fn = tostring_hook[tag(o)] or %tostring
 return fn(o)
end
-- new "tostring" method
function settagmethod_hook.tostring(tag, method, fn)
 tostring_hook[tag] = fn			-- modify tostring()
 settagmethod(tag, "concat", function(a,b)	-- and '..' operator
	return tostring(a)..tostring(b)
 end)
end
-- for convenience
settagmethod(tag_nil, "tostring", function(o)
 return "<nil>"
end)
-- Methods for the vector userdata type.
-- The only things exported from C is the vec-function that
-- creates a new vector and the methods to access the 3 fields
-- x, y, and z.
settagmethod(tag_vec, "tostring", function(o)
 return format("vec(%g, %g, %g)", o.x, o.y, o.z)
end)
settagmethod(tag_vec, "add", function(a,b)
 local ta,tb = tag(a),tag(b)
 if ta==tb then
	return vec(a.x+b.x, a.y+b.y, a.z+b.z)
 end
 error("illegal type for vector-add")
end)
settagmethod(tag_vec, "mul", function(a,b)
 local ta,tb = tag(a),tag(b)
 if ta==tb then
	return vec(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x)
	--return a.x*b.x + a.y*b.y + a.z*b.z
 elseif tb==tag_num then
	return vec(a.x*b, a.y*b, a.z*b)
 end
 error("illegal type for vector-mul")
end)
-- and so on...
v=vec()		-- create new 0-vector
print("v="..v)
v=vec(1,2,3)	-- create new vector
print("v="..v)
u=v 		-- create new reference. u and v are same object!
print("u="..u)
v.x=0.9		-- change one field of v _and_ u!
print("v="..v, "u="..u)
u=vec(v)	-- create copy. now u and v are different objects.
print("u="..u)
u.x=42		-- change one field of u.
print("v="..v, "u="..u)
print("u+v="..u+v)	-- vector addition
print("u*v="..u*v)	-- vector addition
print("u*3="..u*3)	-- vector addition
print("vec(1,1,1)*vec(2,2,2)="..vec(1,1,1)*vec(2,2,2))

AltStyle によって変換されたページ (->オリジナル) /