User Data Example

lua-users home
wiki

Description

This Lua 5.0 example shows how to store a structure in a userdata.

Another approach is to store only a single pointer in the userdata. See UserDataWithPointerExample.

The metatable for the userdata is put in the registry, and the __index field points to the table of methods so that the object:method() syntax will work. The methods table is stored in the table of globals so that scripts can add methods written in Lua.

The Lua functions that use the stucture will need to either access a userdata on the stack, or push a new userdata onto the stack.

checkFoo ensures that a userdata on the stack is the correct type, and returns a pointer to the structure inside the userdata.

pushFoo leaves a new userdata on top of the stack, sets its metatable, and returns a pointer to the structure so that you can fill in the fields.

foo.c C code


#include "lua.h"

#include "lauxlib.h"



#define FOO "Foo"



typedef struct Foo {

  int x;

  int y;

} Foo;





static Foo *toFoo (lua_State *L, int index)

{

  Foo *bar = (Foo *)lua_touserdata(L, index);

  if (bar == NULL) luaL_typerror(L, index, FOO);

  return bar;

}



static Foo *checkFoo (lua_State *L, int index)

{

  Foo *bar;

  luaL_checktype(L, index, LUA_TUSERDATA);

  bar = (Foo *)luaL_checkudata(L, index, FOO);

  if (bar == NULL) luaL_typerror(L, index, FOO);

  return bar;

}



static Foo *pushFoo (lua_State *L)

{

  Foo *bar = (Foo *)lua_newuserdata(L, sizeof(Foo));

  luaL_getmetatable(L, FOO);

  lua_setmetatable(L, -2);

  return bar;

}





static int Foo_new (lua_State *L)

{

  int x = luaL_optint(L, 1, 0);

  int y = luaL_optint(L, 2, 0);

  Foo *bar = pushFoo(L);

  bar->x = x;

  bar->y = y;

  return 1;

}



static int Foo_yourCfunction (lua_State *L)

{

  Foo *bar = checkFoo(L, 1);

  printf("this is yourCfunction\t");

  lua_pushnumber(L, bar->x);

  lua_pushnumber(L, bar->y);

  return 2;

}



static int Foo_setx (lua_State *L)

{

  Foo *bar = checkFoo(L, 1);

  bar->x = luaL_checkint(L, 2);

  lua_settop(L, 1);

  return 1;

}



static int Foo_sety (lua_State *L)

{

  Foo *bar = checkFoo(L, 1);

  bar->y = luaL_checkint(L, 2);

  lua_settop(L, 1);

  return 1;

}



static int Foo_add (lua_State *L)

{

  Foo *bar1 = checkFoo(L, 1);

  Foo *bar2 = checkFoo(L, 2);

  Foo *sum  = pushFoo(L);

  sum->x = bar1->x + bar2->x;

  sum->y = bar1->y + bar2->y;

  return 1;

}



static int Foo_dot (lua_State *L)

{

  Foo *bar1 = checkFoo(L, 1);

  Foo *bar2 = checkFoo(L, 2);

  lua_pushnumber(L, bar1->x * bar2->x + bar1->y * bar2->y);

  return 1;

}



static const luaL_reg Foo_methods[] = {

  {"new",           Foo_new},

  {"yourCfunction", Foo_yourCfunction},

  {"setx",          Foo_setx},

  {"sety",          Foo_sety},

  {"add",           Foo_add},

  {"dot",           Foo_dot},

  {0, 0}

};



static int Foo_gc (lua_State *L)

{

  printf("bye, bye, bar = %p\n", toFoo(L, 1));

  return 0;

}



static int Foo_tostring (lua_State *L)

{

  char buff[32];

  sprintf(buff, "%p", toFoo(L, 1));

  lua_pushfstring(L, "Foo (%s)", buff);

  return 1;

}



static const luaL_reg Foo_meta[] = {

  {"__gc",       Foo_gc},

  {"__tostring", Foo_tostring},

  {"__add",      Foo_add},

  {0, 0}

};



int Foo_register (lua_State *L)

{

  luaL_openlib(L, FOO, Foo_methods, 0);  /* create methods table,

                                            add it to the globals */

  luaL_newmetatable(L, FOO);          /* create metatable for Foo,

                                         and add it to the Lua registry */

  luaL_openlib(L, 0, Foo_meta, 0);    /* fill metatable */

  lua_pushliteral(L, "__index");

  lua_pushvalue(L, -3);               /* dup methods table*/

  lua_rawset(L, -3);                  /* metatable.__index = methods */

  lua_pushliteral(L, "__metatable");

  lua_pushvalue(L, -3);               /* dup methods table*/

  lua_rawset(L, -3);                  /* hide metatable:

                                         metatable.__metatable = methods */

  lua_pop(L, 1);                      /* drop metatable */

  return 1;                           /* return methods on the stack */

}





int main(int argc, char *argv[])

{

  lua_State *L = lua_open();



  luaopen_base(L);

  luaopen_table(L);

  luaopen_io(L);

  luaopen_string(L);

  luaopen_math(L);

  luaopen_debug(L);



  Foo_register(L);

  lua_pop(L, 1); //After foo register the methods are still on the stack, remove them.



  if(argc>1) lua_dofile(L, argv[1]);



  lua_close(L);

  return 0;

}

Compiling the Code

This code can be compiled for Lua 5.0 as follows:


gcc foo.c  -L/usr/local/lib -llua -llualib

Lua Test Code

for n,v in Foo do print(n,v) end



local a = Foo.new()

local b = Foo.new(99,100)



MyFunction = Foo.yourCfunction



print(a, MyFunction(a))

print(b, MyFunction(b))





function Foo:show(msg)

  print(msg, self, self:yourCfunction())

  return self

end



function Foo:point(t)

  assert(type(t) == 'table')

  self:setx(t.x or t[1]):sety(t.y or t[2])

  return self

end



setmetatable(Foo, { __call = function(self, x, y)

                               local bar = self.new(x,y)

                               print('created', bar)

                               return bar

                             end } )



local p = Foo(1,2)

p:show('p is')

p:setx(3):show'p is':sety(4):show'p is'

p:point{33,44}:show'p is'

p = nil



collectgarbage()



a:point{x=500, y=1000}

a:show'a is'



r = Foo.add(a,b)

r:show'r is'



a:show'a is'

b:show'b is'

s = a + b

s:show's is'



--debug.debug()

Test Code Output


$ ./a test.lua

sety    function: 0xa045388

dot     function: 0xa045328

setx    function: 0xa044fb8

yourCfunction   function: 0xa0452c8

add     function: 0xa0452f8

new     function: 0xa044f80

this is yourCfunction   Foo (0xa046938) 0       0

this is yourCfunction   Foo (0xa046760) 99      100

created Foo (0xa045458)

this is yourCfunction   p is    Foo (0xa045458) 1       2

this is yourCfunction   p is    Foo (0xa045458) 3       2

this is yourCfunction   p is    Foo (0xa045458) 3       4

this is yourCfunction   p is    Foo (0xa045458) 33      44

bye, bye, bar = 0xa045458

this is yourCfunction   a is    Foo (0xa046938) 500     1000

this is yourCfunction   r is    Foo (0xa045478) 599     1100

this is yourCfunction   a is    Foo (0xa046938) 500     1000

this is yourCfunction   b is    Foo (0xa046760) 99      100

this is yourCfunction   s is    Foo (0xa046470) 599     1100

bye, bye, bar = 0xa046470

bye, bye, bar = 0xa045478

bye, bye, bar = 0xa046760

bye, bye, bar = 0xa046938

Modifications for use with Lua 5.1.1

Replace the luaopen functions in main():

  luaopen_base(L);

  luaopen_table(L);

  luaopen_io(L);

  luaopen_string(L);

  luaopen_math(L);

  luaopen_debug(L);

with:


  luaL_openlibs(L);

to avoid problems with luaopen_io(L). See [this list message] for details.

Replace (in main())


if(argc>1) lua_dofile(L, argv[1]);

with:


if (argc > 1) {

   int s = luaL_loadfile(L, argv[1]);

   if (s == 0) {

      s = lua_pcall(L, 0, LUA_MULTRET, 0);

   }

}

To compile (5.1.4)


gcc foo.c  -L/usr/local/lib -llua -ldl -lm

The first line of test.lua needs to be modified to use the pairs() function:


for n,v in pairs(Foo) do print(n,v) end

I think it would be better to just rewrite the page for 5.1. --JohnBelmonte

RecentChanges · preferences
edit · history
Last edited October 7, 2010 7:12 pm GMT (diff)