Background
I'm using Lua with luaglut to do some OpenGL stuff. The luaglut API is almost identical to the gl/glut C APIs. Sometimes, gl functions want a pointer to some data, for example:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, oh_crap_a_pointer)
In these cases, the luaglut bindings take a light userdata containing the pointer. In the luaglut demos, a small example library called memarray does the work of setting up a C array and returning a pointer to it as a light userdata.
You might use "memarray" something like this:
require 'memarray'
function loadTexture(filename)
local file = assert(io.open(filename, 'rb'))
local data = file:read('*a')
file:close()
local array = memarray('uchar', #data)
array:from_str(data)
return array
end
-- ... later
local texture = loadTexture('whatever.rgba')
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA,
GL_UNSIGNED_BYTE, texture:ptr())
That got me thinking, why do we need memarray at all? Lua strings should be binary safe; all we really need is to get a pointer to the string returned from
file:read('*a')
as a light userdata. It would look something like this:
function loadTexture(filename)
local file = assert(io.open(filename, 'rb'))
local data = file:read('*a')
file:close()
return data
end
-- ... later
local stringpointer = require 'stringpointer'
local texture = loadTexture('whatever.rgba')
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA,
GL_UNSIGNED_BYTE, stringpointer.get(texture))
Of course we pass the string around instead of the pointer, because the GC could free the memory the string was in if there are no more references to it, invalidating the pointer. Internally Lua strings are immutable and passed by reference anyway, so this is no big deal.
Code
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
int get_pointer(lua_State *L)
{
luaL_checktype(L, 1, LUA_TSTRING);
lua_pushlightuserdata(L, (void *)lua_tostring(L, 1));
return 1;
}
int luaopen_stringpointer(lua_State *L)
{
const luaL_Reg api[] = {
{"get", get_pointer},
{NULL, NULL}
};
#if LUA_VERSION_NUM == 501
luaL_register(L, "stringpointer", api);
#else
luaL_newlib(L, api);
#endif
return 1;
}
It's really tiny, but my C is rusty so maybe I did something stupid. It seems to work fine so far. Any input is welcome.
int get_pointer(lua_State *L)
andint luaopen_stringpointer(lua_State *L)
are not void functions? I cannot tell from the context if they have to return ints or not. \$\endgroup\$get_pointer
is the number of return values it gives when you call it from Lua. That means return the last thing on the stack, the lightuserdata pushed on the line before. \$\endgroup\$luaopen_stringpointer
is the function Lua looks for when yourequire 'stringpointer'
. The return value means the same thing there, it returns one value, a table containing a "get" function. \$\endgroup\$glTexImage2D
do with the string data passed to it? Does it own it? Does it free it? Does it use it immediately? Does it use it at some, unspecified, later date? \$\endgroup\$