User Data With Pointer Example |
|
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:
colorallocate
takes three numbers for RGB, and returns a color index.
line
draw a line between two points.
PNG
draws the image and saves it to a file.
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.
#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; }
This code can be compiled for Lua 5.0 as follows:
gcc gd.c -L/usr/local/lib/ -llua -llualib -lgd -lpng
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()
$ ./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.