lua-users home
lua-l archive

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