User Data With Pointer Example

lua-users home
wiki

Description

If the structures that you want to manipulate from Lua need to be allocated or created by C/C++ code, then it's best to just store a pointer to the structure inside the userdata.

For example, when creating images with Thomas Boutell's GD Graphics Library[1], the function gdImageCreate only returns a pointer to an image object.

Image.new is a constructor that returns a userdata containing a pointer the image to be manipulated. The userdata's metatable has a garbage collection event to destroy the image, and an index event for calling the image methods.

Only three methods are implemented:

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 manipulate the Image will need to either access a userdata on the stack, or push a new userdata onto the stack.

checkImage ensures that a userdata on the stack is the correct type, and returns the Image pointer inside the userdata.

pushImage leaves a new userdata on top of the stack, sets its metatable, and sets the Image pointer inside the userdata.

C code

#include <stdio.h>

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "gd.h"

/*
==============================================================================
  Example Lua bindings for GD
==============================================================================
*/


#define IMAGE "Image"

typedef gdImagePtr Image;


static Image toImage (lua_State *L, int index)
{
  Image *pi = (Image *)lua_touserdata(L, index);
  if (pi == NULL) luaL_typerror(L, index, IMAGE);
  return *pi;
}

static Image checkImage (lua_State *L, int index)
{
  Image *pi, im;
  luaL_checktype(L, index, LUA_TUSERDATA);
  pi = (Image*)luaL_checkudata(L, index, IMAGE);
  if (pi == NULL) luaL_typerror(L, index, IMAGE);
  im = *pi;
  if (!im)
    luaL_error(L, "null Image");
  return im;
}

static Image *pushImage (lua_State *L, Image im)
{
  Image *pi = (Image *)lua_newuserdata(L, sizeof(Image));
  *pi = im;
  luaL_getmetatable(L, IMAGE);
  lua_setmetatable(L, -2);
  return pi;
}


static int Image_new (lua_State *L)
{
  int x = luaL_checkint(L, 1);
  int y = luaL_checkint(L, 2);
  pushImage(L, gdImageCreate(x, y));
  return 1;
}

static int Image_color_allocate (lua_State *L)
{
  Image im = checkImage(L, 1);
  int r = luaL_checkint(L, 2);
  int g = luaL_checkint(L, 3);
  int b = luaL_checkint(L, 4);
  lua_pushnumber(L, gdImageColorAllocate(im, r, g, b));
  return 1;
}

static int Image_line (lua_State *L)
{
  Image im = checkImage(L, 1);
  int x1     = luaL_checkint(L, 2);
  int y1     = luaL_checkint(L, 3);
  int x2     = luaL_checkint(L, 4);
  int y2     = luaL_checkint(L, 5);
  int colour = luaL_checkint(L, 6);
  gdImageLine(im, x1, y1, x2, y2, colour);
  return 0;
}

static int Image_png (lua_State *L)
{
  /* Output the image to the disk file in PNG format. */
  Image im         = checkImage(L, 1);
  const char *name = luaL_checkstring(L, 2);
  FILE *pngout     = fopen( name, "wb");
  gdImagePng(im, pngout);
  fclose(pngout);
  return 0;
}


static const luaL_reg Image_methods[] = {
  {"new",           Image_new},
  {"colorallocate", Image_color_allocate},
  {"line",          Image_line},
  {"PNG",           Image_png},
  {0,0}
};


static int Image_gc (lua_State *L)
{
  Image im = toImage(L, 1);
  if (im) gdImageDestroy(im);
  printf("goodbye Image (%p)\n", lua_touserdata(L, 1));
  return 0;
}

static int Image_tostring (lua_State *L)
{
  lua_pushfstring(L, "Image: %p", lua_touserdata(L, 1));
  return 1;
}

static const luaL_reg Image_meta[] = {
  {"__gc",       Image_gc},
  {"__tostring", Image_tostring},
  {0, 0}
};


int Image_register (lua_State *L)
{
  luaL_openlib(L, IMAGE, Image_methods, 0);  /* create methods table,
                                                add it to the globals */
  luaL_newmetatable(L, IMAGE);        /* create metatable for Image,
                                         add it to the Lua registry */
  luaL_openlib(L, 0, Image_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);

  Image_register(L);

  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 gd.c  -L/usr/local/lib/ -llua -llualib -lgd -lpng

Lua Test Code

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

size = 256

im = Image.new(size, size)

print(im)

white = im:colorallocate(255, 255, 255)

for i = 0,size-1,1 do
  c = im:colorallocate(0, i, i)
  im:line(0, 0, size-1 , i, c)
end

im:PNG'test.png'

-- debug.debug()

Test Code Output

$ ./a gd.lua
line    function: 0x10054ff0
PNG     function: 0x100553b8
colorallocate   function: 0x100552f8
new     function: 0x10054fb8
Image (0x10055ef0)
goodbye Image (0x10055ef0)

This is the image created by the test code.

Related Pages


RecentChanges · preferences
edit · history
Last edited January 6, 2007 6:31 pm GMT (diff)