Binding With Metatable And Closures

lua-users home
wiki

Showing revision 5

Description

Here is an example of how to make a Lua binding to Thomas Boutell's GD Graphics Library[1] using closures containing a metatable. The original example of how to use GD in C can be found on Tom's web page[2].

Image.Create is a constructor that returns a userdata containing the image to be maninpulated. The userdata's metatable has a garbage collection event to destroy the image, and a gettable event for calling the methods needed to draw the image and save the image to a file. Only three methods are implimented: ColorAllocate, Line and PNG.

C code


#include "lua.h"
#include "lauxlib.h"
#include "gd.h"
#include <stdio.h>

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

static gdImagePtr check_image (lua_State *L, int index)
{
  luaL_check_type(L, index, LUA_TUSERDATA);
  lua_getmetatable(L, index);
  if( ! lua_equal(L, lua_upvalueindex(1), -1) )
    luaL_typerror(L, index, "Image");  /* die */
  lua_pop(L, 1);
  return (gdImagePtr)lua_unboxpointer(L, index);
}

static int image_create (lua_State *L)
{
  int x = luaL_check_int(L, 1);
  int y = luaL_check_int(L, 2);
  gdImagePtr im = gdImageCreate(x, y);
  lua_boxpointer(L, im);
  lua_pushvalue(L, lua_upvalueindex(1));
  lua_setmetatable(L, -2);
  return 1;
}

static int image_destroy (lua_State *L)
{
  gdImagePtr im = (gdImagePtr)lua_unboxpointer(L,1);
  gdImageDestroy(im);
  printf("Goodbye!\n");
  return 0;
}

static int image_color_allocate (lua_State *L)
{
  gdImagePtr im = check_image(L, 1);
  int r = luaL_check_int(L, 2);
  int g = luaL_check_int(L, 3);
  int b = luaL_check_int(L, 4);
  lua_pushnumber(L, gdImageColorAllocate(im, r, g, b));
  return 1;
}

static int image_line (lua_State *L)
{
  gdImagePtr im = check_image(L, 1);
  int x1     = luaL_check_int(L, 2);
  int y1     = luaL_check_int(L, 3);
  int x2     = luaL_check_int(L, 4);
  int y2     = luaL_check_int(L, 5);
  int colour = luaL_check_int(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. */
  gdImagePtr im    = check_image(L, 1);
  const char *name = luaL_check_string(L, 2);
  FILE *pngout     = fopen( name, "wb");
  gdImagePng(im, pngout);
  fclose(pngout);
  return 0;
}

static const luaL_reg meta_methods[] = {
{"__gc", image_destroy },
{0,0}
};

static const luaL_reg image_methods[] = {
{"Create",        image_create},
{"ColorAllocate", image_color_allocate},
{"Line",          image_line},
{"PNG",           image_png},
{0,0}
};

#define newtable(L) (lua_newtable(L), lua_gettop(L))

int openGDlua (lua_State *L)
{
  int metatable, methods;

  printf("Hi there!\n");

  lua_pushliteral(L, "Image");       /* name of Image table */
  methods   = newtable(L);           /* Image methods table */
  metatable = newtable(L);           /* Image metatable */
  lua_pushliteral(L, "__index");
  lua_pushvalue(L, methods);         /* reference to Image methods */
  lua_settable(L, metatable);        /* add gettable event to metatable */
  luaL_openlib(L, 0, meta_methods,  0); /* fill metatable */
  luaL_openlib(L, 0, image_methods, 1); /* fill Image methods table */
  lua_settable(L, LUA_GLOBALSINDEX); /* add Image to globals */
  return 0;
}

Compiling the Code

This code can be compiled for Lua 5.0 (alpha) as follows:

gcc -fPIC -g -c GDlua.c -o GDlua.o
gcc -g -shared -Wl,-soname,libgdlua.so -o libgdlua.so.1.0.0 GDlua.o -L/usr/local/lib/ -llua -llualib -lgd

Lua Test Code


assert(loadlib('/usr/local/lib/libgdlua.so','openGDlua'))()

size = 256

im = Image.Create(size,size)

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'

------------------------------------------------------------------------------

print('= Image =',Image)
for n,v in pairs(Image) do print(n,v) end
mt = getmetatable(im)
print('= metatable =',mt)
for n,v in pairs(mt) do print(n,v) end

Test Code Output

$ lua test_gd.lua 
Hi there!
= Image =       table: 0x8066868
Line    function: 0x8066980
Create  function: 0x80668b8
ColorAllocate   function: 0x80668d8
PNG     function: 0x80669a0
= metatable =   table: 0x8066890
__gettable      table: 0x8066868
__gc    function: 0x80668f8
Goodbye!

This is the image created by the test code.

The next example shows how to use the gettable and settable events to read and write structure members BindingWithMembersAndMethods


RecentChanges · preferences
edit · history · current revision
Edited February 7, 2003 9:01 pm GMT (diff)