[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: LuaJIT FFI __gc metamethod?
- From: "Robert G. Jakabosky" <bobby@...>
- Date: Sun, 27 Feb 2011 19:39:33 -0800
On Sunday 27, CrazyButcher wrote:
> I try to box the content of a cdata in my own userdata via a classic
> lua plugin function, to which I can hand the metatable for the
> userdata.
>
> lua_topointer -> gives pointer to cdata obj, not the pointer it stores
> lua_touserdata -> fails on cdata
>
> lua_tonumber -> works but it feels weird
>
> I create the number in the lua code through
> tonumber(ffi.cast("uintptr_t",cdata))
>
> and on C side
> const void orig = (const void*)(uintptr_t)lua_tonumber(...)
I do not think this would be 64bit safe. It might work most of the time if
all your 64bit pointer only use the lower 48bits.
For the FFI-based bindings generated by LuaNativeObjects [1], I exported
(private to the FFI bindings code) a C function which takes two arguments size
& a metatable and returns a new userdata object. Then in the FFI bindings
code the userdata object is cast to a structure pointer.
/* c code. */
static int nobj_udata_new_ffi(lua_State *L) {
size_t size = luaL_checkinteger(L, 1);
void *ud;
luaL_checktype(L, 2, LUA_TTABLE);
lua_settop(L, 2);
/* create userdata. */
ud = lua_newuserdata(L, size);
lua_replace(L, 1);
/* set userdata's metatable. */
lua_setmetatable(L, 1);
return 1;
}
-- lua side FFI code
ffi.cdef[[
typedef struct obj_udata {
void *obj;
int flags;
} obj_udata;
]]
-- create new userdata wrapped cdata object.
function new_cdata_obj(cdata, size)
local meta = {} -- new metatable or use some existing metatable
if not size then
local udata = nobj_udata_new_ffi(ffi.sizeof('obj_udata'), meta)
local obj_data = ffi.cast('obj_udata *')
obj_data.obj = cdata -- cdata pointer.
obj_data.flags = 0
else
local udata = nobj_udata_new_ffi(size, meta)
local obj_data = ffi.cast('void *')
ffi.copy(obj_data, cdata, size)
end
-- return userdata
return udata, meta
end
Also note that the object's methods will receive the userdata object as the
'self' argument and it will need to be cast back to a cdata value and you
should check the metatable on the userdata to make sure it is the right type
(someone could pass any other userdata object can cause bad things to happen).
If you don't do type checking, then your bindings can't be used in a sandbox
safely.
For performance of the metatable type checking, I use a weak table per object
type that maps each userdata value to the wrapped cdata value.
local function obj_udata_luacheck_internal(obj, type_mt)
-- need the debug version of getmetatable, since the metatable might be
-- hidden (i.e. if '__metatable' is set)
local obj_mt = debug.getmetatable(obj)
if obj_mt == type_mt then
-- convert userdata to cdata.
local ud = ffi.cast('obj_data *',obj)
return ud.obj
end
error("(expected `" .. type_mt['.name'] .. "`, got " .. type(obj) .. ")", 2)
end
-- '${object_name}' is replaced with the cdata C typename.
local ${object_name}_objects[udata] = setmetatable({}, { __mode = "k" })
local function obj_type_${object_name}_check(udata)
local c_obj = ${object_name}_objects[udata]
if c_obj == nil then
-- cdata object not in cache
c_obj = obj_udata_luacheck(udata, ${object_name}_mt)
c_obj = ffi.cast("${object_name} *", c_obj) -- cast from 'void *'
${object_name}_objects[udata] = c_obj -- cache object
end
return c_obj
end
1. https://github.com/Neopallium/LuaNativeObjects
--
Robert G. Jakabosky