Binding With Metatable And Closures |
|
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.
#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;
}
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
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
$ 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