[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Conceptual problem with gettable tag method [repost]
- From: Edgar Toernig <froese@...>
- Date: Thu, 15 Jun 2000 04:52:41 +0200
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))